(Compose UI) - 键盘(IME)遮挡了应用程序的内容

3

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

假设我们有三个不同的对话框(可以是任何内容),看起来像这样:

enter image description here

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

enter image description here

无法查看用户编写的内容。以下是我的代码:

@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 Compose视图的内容

到目前为止,我已经找到了解决此问题的方法,并将我的方法作为答案分享。


这个回答解决了你的问题吗?软键盘覆盖Jetpack Compose视图内容 - Phil Dukhov
你不应该因为当前的答案不适合你就创建另一个问题。相反,你应该留下自己的答案。 - Phil Dukhov
@PhilipDukhov,这并没有回答我的问题,我已经提到了。请仔细阅读我的问题。主要区别在于,我不想创建可滚动布局,而是将其保留为原样(但我使用此方法作为技巧)。此外,我在视图中不显示任何列表。这些是完全不同的问题。 - Drogheda
3个回答

7

我的解决问题的方法是使用Jetpack Compose中的Insets

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)

这意味着我们的Column的高度将完美适应我们的屏幕。因此,当键盘未打开时,Column将不可滚动。
以下是完整代码:
@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") }
        )
    }
}

最终的代码使我们能够向下滚动视图:

enter image description here

API 30以下的重要提示

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

在<activity中,您需要添加android:windowSoftInputMode="adjustResize"以使其正常工作。它不会调整您的组件大小,但是这种方法必须使用。

清单应如下所示:

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

随意给我提供任何建议,帮助我改进我的问题。据我所知,这个问题和安卓一样古老,我想创建一个快速教程来管理它。愉快编码!


1
你正在使用 fillMaxWidth() 和 fillMaxSize(),我认为你忘记移除 fillMaxWidth() 了。 - Edhar Khimich
2
Accompanist 已被弃用。 - Kristy Welsh
你好。 在我的情况下,我必须处理项目中的两种情况:例子: 情况1:需要调整屏幕大小(AdjustResize) 情况2:不需要调整屏幕大小(AdjustResize)如果我们在清单文件和活动中设置了这些内容,我该如何处理我的项目中的这两种情况:注意:请假设两者是不同的组合和屏幕。 - Tippu Fisal Sheriff
@TippuFisalSheriff,你找到解决方案了吗?我也需要仅在某些组合屏幕上使用AdjustResize,其中它将正常工作(可滚动的消息列表和位于屏幕底部的输入字段以键入新消息),但对于其他屏幕,它会破坏逻辑并使输入字段出现在键盘后面(例如登录、注册,在这些屏幕上输入字段位于中心)。 - user924
然而,我仍未找到以上问题的解决方案。 - Tippu Fisal Sheriff

4

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

build.gradle(:project)中

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

在`build.gradle`(:app)中
...
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")
        }
    }
}

你好, 在我的项目中,我必须处理以下两种情况:示例: 情况1:需要调整屏幕大小(AdjustResize) 情况2:不需要调整屏幕大小(AdjustResize)如果我们在清单文件和活动中设置了这些内容,我该如何处理这两种情况:注意:假设这两个是不同的组件和屏幕。 - Tippu Fisal Sheriff
@TippuFisalSheriff 我猜唯一的方法就是在不同的活动中进行组合。我不知道有没有办法在运行时更改这个。让我知道你发现了什么。 - RealityExpander
好的,我们采用单一活动架构。 - Tippu Fisal Sheriff
@TippuFisalSheriff 同感。我也不确定是否有解决方法,我只看到过“修改清单文件”的解决方案,这似乎相当古老,就像大多数 Android 库和技术一样! - RealityExpander
无法理解,请您能详细解释一下吗? - Tippu Fisal Sheriff
我所知道的唯一调整大小/平移的方法是在AndroidManifest.xml文件中进行。除此之外,我不确定如何... - RealityExpander

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
    )

你好, 在我的项目中,我必须处理以下两种情况:示例: 情况1:需要调整屏幕大小(AdjustResize) 情况2:不需要调整屏幕大小(AdjustResize)如果我们在清单文件和活动中设置了这些内容,我该如何处理这两种情况:注意:假设这两个是不同的组件和屏幕。 - Tippu Fisal Sheriff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接