(撰写 UI)- 键盘 (IME) 与应用程序的内容重叠

问题描述 投票:0回答:4

几天前,我遇到了一个问题,我的视图的一部分被键盘重叠了。

假设我们有 3 个不同的对话框(可以是任何内容),如下所示:

当我想写任何东西时,最后一个对话框被键盘覆盖:

并且无法查看用户写的内容。这是我的代码:

@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {

        Column(
            modifier = Modifier
                .fillMaxWidth()
                .background(PrimaryLight)
                .fillMaxSize()

        ) {
            BuildWordsScreenContents()
        }

}

@Composable
fun BuildWordsScreenContents() {

    Column(
        Modifier
            .fillMaxSize()
            .padding(all = 16.dp)

    ) {

        val inputBoxModifier = Modifier
            .clip(RoundedCornerShape(10.dp))
            .background(Primary)
            .weight(12f)
            .wrapContentHeight()

        InputBlock("Dialog1", inputBoxModifier)
        Spacer(Modifier.weight(1f))
        InputBlock("Dialog2", inputBoxModifier)
        Spacer(Modifier.weight(1f))
        InputBlock("Dialog3", inputBoxModifier)
    }
}



@Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
    Column(modifier = inputBlockModifier) {
        Text(
            dialogText,
            fontSize = 30.sp,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentSize(Alignment.Center)
        )
        var text by remember { mutableStateOf("") }

        TextField(
            value = text,
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentSize(Alignment.Center),
            onValueChange = { text = it },
            label = { Text("Label") }
        )
    }
}

这个问题似乎与我的类似,但答案修改了我想避免的视图内容:

软件键盘与jetpack撰写视图的内容重叠

现在我已经知道如何解决这个问题了,我分享我的方法作为答案

android android-layout android-jetpack-compose jetpack-compose-accompanist
4个回答
7
投票

我处理这个问题的方法是使用 Insets for Jetpack Compose:

https://google.github.io/accompanist/insets/

为了开始处理问题,您需要向 gradle 添加依赖项(当前版本是 0.22.0-rc)。

dependencies { 
    implementation "com.google.accompanist:accompanist-insets:0.22.0-rc"
}

然后您需要使用

ProvideWindowInsets

将您的内容包装在您的活动中
setContent {
    ProvideWindowInsets {
        YourTheme {
            //YOUR CONTENT HERE
        }
    }
}

此外,您需要在活动 onCreate() 函数中添加以下行:

WindowCompat.setDecorFitsSystemWindows(window, false)

更新:尽管建议使用此功能,但根据我的经验,它可能会使此方法不起作用。如果您遇到任何问题,您可能需要删除此行。

现在您的项目已设置为使用Insets

在接下来的步骤中,我将使用我在问题中提供的代码

首先,您需要用

包裹您的主栏

ProvideWindowInsets(windowInsetsAnimationsEnabled = true)

然后让我们稍微修改一下修饰符,添加:

.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())

正如你所看到的,我的方法中的技巧是使用verticalScroll()。主栏的最终代码应如下所示:

@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {

    ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {

        Column(
            modifier = Modifier
                .fillMaxWidth()
                .background(PrimaryLight)
                .statusBarsPadding()
                .navigationBarsWithImePadding()
                .verticalScroll(rememberScrollState())
                .fillMaxSize()

        ) {
            BuildWordsScreenContents()
        }
    }
}

现在我们来修改

fun BuildWordsScreenContents()

中Column的修饰符

主要修改是我们通过以下方式提供屏幕高度:

.height(LocalConfiguration.current.screenHeightDp.dp)

这意味着我们的柱的高度将完美适合我们的屏幕。 因此,当键盘未打开时,该列将无法滚动

有完整代码:

@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {

    ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {

        Column(
            modifier = Modifier
                .fillMaxWidth()
                .background(PrimaryLight)
                .statusBarsPadding()
                .navigationBarsWithImePadding()
                .verticalScroll(rememberScrollState())
                .fillMaxSize()

        ) {
            BuildWordsScreenContents()
        }
    }
}

@Composable
fun BuildWordsScreenContents() {

    Column(
        Modifier
            .height(LocalConfiguration.current.screenHeightDp.dp)
            .padding(all = 16.dp)

    ) {

        val inputBoxModifier = Modifier
            .clip(RoundedCornerShape(10.dp))
            .background(Primary)
            .weight(12f)
            .wrapContentHeight()

        InputBlock("Dialog1", inputBoxModifier)
        Spacer(Modifier.weight(1f))
        InputBlock("Dialog2", inputBoxModifier)
        Spacer(Modifier.weight(1f))
        InputBlock("Dialog3", inputBoxModifier)
    }
}





@Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
    Column(modifier = inputBlockModifier) {
        Text(
            dialogText,
            fontSize = 30.sp,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentSize(Alignment.Center)
        )
        var text by remember { mutableStateOf("") }

        TextField(
            value = text,
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentSize(Alignment.Center),
            onValueChange = { text = it },
            label = { Text("Label") }
        )
    }
}

最终代码允许我们向下滚动视图:

API 30-

的重要说明

对于低于 30 的 API,您需要修改 AndroidManifest.xml 文件

android:windowSoftInputMode="adjustResize"中才能使其发挥作用。它不会调整组件的大小,但必须使这种方法发挥作用

清单应如下所示:

<activity
    android:name=".MainActivity"
    android:windowSoftInputMode="adjustResize"

请随时给我任何如何改进我的问题的提示。 AFAIK 这个问题和 android 一样古老,我想创建一个快速教程如何管理它。快乐编码!


4
投票

这是我的解决方案,使用 Compose 1.2.0 中的实验功能

build.gradle
(:项目)

...
    ext {
        compose_version = '1.2.0-beta03'
    }
...

build.gradle
(:应用程序)

...
dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation "androidx.compose.foundation:foundation-layout:$compose_version"
...
}

AndroidManifest.xml

         <activity
            ...
            android:windowSoftInputMode="adjustResize" >

AuthScreen.kt

@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun AuthScreen(

    val focusManager = LocalFocusManager.current
    val coroutineScope = rememberCoroutineScope()

    // Setup the handles to items to scroll to.
    val bringIntoViewRequesters = mutableListOf(remember { BringIntoViewRequester() })
    repeat(6) {
        bringIntoViewRequesters += remember { BringIntoViewRequester() }
    }
    val buttonViewRequester = remember { BringIntoViewRequester() }


    fun requestBringIntoView(focusState: FocusState, viewItem: Int) {
        if (focusState.isFocused) {
            coroutineScope.launch {
                delay(200) // needed to allow keyboard to come up first.
                if (viewItem >= 2) { // force to scroll to button for lower fields
                    buttonViewRequester.bringIntoView()
                } else {
                    bringIntoViewRequesters[viewItem].bringIntoView()
                }
            }
        }
    }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top,
        modifier = Modifier
            .fillMaxSize()
            .statusBarsPadding()
            .navigationBarsPadding()
            .imePadding()
            .padding(10.dp)
            .verticalScroll(rememberScrollState())

    ) {

        repeat(6) { viewItem ->
            Row(
                modifier = Modifier
                    .bringIntoViewRequester(bringIntoViewRequesters[viewItem]),
            ) {
                TextField(
                    value = "",
                    onValueChange = {},
                    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
                    keyboardActions = KeyboardActions(
                        onNext = { focusManager.moveFocus(FocusDirection.Down) }),
                    modifier = Modifier
                        .onFocusEvent { focusState ->
                            requestBringIntoView(focusState, viewItem)
                        },
                )
            }
        }


        Button(
            onClick = {},
            modifier = Modifier
                .bringIntoViewRequester(buttonViewRequester)
        ) {
            Text(text = "I'm Visible")
        }
    }
}

0
投票

尝试用谷歌搜索这样的关键字:Modifier.statusBarsPadding()、systemBarsPadding()、navigationBarsPadding()。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        makeStatusBarTransparent()
        //WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            Box(
                Modifier
                    .background(Color.Blue)
                    .fillMaxSize()
                    .padding(top = 10.dp, bottom = 10.dp)
                    .statusBarsPadding() //systemBarsPadding
            ) {
                //Box(Modifier.background(Color.Green).navigationBarsPadding()) {
                Greeting("TopStart", Alignment.TopStart)
                Greeting("BottomStart", Alignment.BottomStart)
                Greeting("TopEnd", Alignment.TopEnd)
                Greeting("BottomEnd", Alignment.BottomEnd)
                //}
            }
        }

/*        setContent {
            MyComposeApp1Theme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
                    Box(Modifier
                            .fillMaxSize()
                            .padding(top = 34.dp)
                    ) {
                        Greeting("Android")
                    }
                }
            }
        }*/
    }
}

@Composable
fun Greeting(name: String, contentAlignment: Alignment) {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = contentAlignment
    ) {
        Text(
            text = "Hello $name!",
            Modifier
                .background(color = Color.Cyan)
        )

    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyComposeApp1Theme {
        Greeting("Android", Alignment.TopStart)
    }
}

@Suppress("DEPRECATION")
fun Activity.makeStatusBarTransparent() {
    window.apply {
        clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        decorView.systemUiVisibility =
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        statusBarColor = android.graphics.Color.GREEN//android.graphics.Color.TRANSPARENT
    }
}

val Int.dp
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        toFloat(),
        Resources.getSystem().displayMetrics
    )

0
投票

我确实喜欢这样:

  1. 确保您

    AndroidMainifest.xml
    设置:
    android:windowSoftInputMode="adjustResize"

  2. 添加此功能(在屏幕内或在其他文件中):

@Composable
fun rememberImeState(): State<Boolean> {
    val imeState = remember {
        mutableStateOf(false)
    }

    val view = LocalView.current
    DisposableEffect(view) {
        val listener = ViewTreeObserver.OnGlobalLayoutListener {
            val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
                ?.isVisible(WindowInsetsCompat.Type.ime()) ?: true
            imeState.value = isKeyboardOpen
        }

        view.viewTreeObserver.addOnGlobalLayoutListener(listener)
        onDispose {
            view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }
    return imeState
}
  1. 将其添加到可组合函数上方:
    val imeState = rememberImeState()
    val scrollState = rememberScrollState()

    LaunchedEffect(key1 = imeState.value) {
        if (imeState.value) {
            scrollState.animateScrollTo(scrollState.maxValue, tween(300))
        }
    }

     Column(
        modifier = modifier.verticalScroll(scrollState),
        verticalArrangement = Arrangement.Top,
        horizontalAlignment = CenterHorizontally
    ) {
        UserSelection(
            onClick = { onClick(it) }
        )
    }

© www.soinside.com 2019 - 2024. All rights reserved.