如何在Android Jetpack Compose中防止键盘和底部对齐按钮之间出现间隙?

huangapple go评论89阅读模式
英文:

How to prevent a gap between the keyboard and a bottom-aligned button in Android Jetpack Compose?

问题

你可以尝试在你的代码中使用Modifier.fillMaxHeight()来确保按钮始终位于屏幕底部,并且在键盘打开时与键盘一起上移,关闭时下移。在以下的代码片段中,我已经添加了这个修饰符:

Column(
    modifier = Modifier
        .fillMaxSize()
        .imePadding()
        .semantics { contentDescription = screenDescription },
    verticalArrangement = Arrangement.SpaceBetween,
    horizontalAlignment = Alignment.CenterHorizontally
) {
    // ... 省略其余的代码

    Column(
        modifier = Modifier
            .weight(0.20f, fill = true)
            .fillMaxWidth()
            .fillMaxHeight() // 这里添加了Modifier.fillMaxHeight()
    ) {
        Crossfade(targetState = isInError, label = "isInErrorButton") { isInError ->
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(48.dp),
                shape = RectangleShape,
                colors = if (isInError) {
                    ButtonDefaults.buttonColors(containerColor = Color.Red)
                } else {
                    ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary)
                },
                onClick = {
                    hapticGenerator.performHapticFeedback(HapticFeedbackType.LongPress)
                    if (validateInput()) {
                        focusManager.clearFocus()
                        onContinueButtonClick()
                        navigateToNextScreen()
                    }
                }
            ) {
                Text(
                    modifier = Modifier.padding(horizontal = 16.dp),
                    text = "Continue",
                    style = MaterialTheme.typography.bodyLarge
                )
            }
        }
    }
}

这个修饰符应该帮助你解决键盘打开和关闭时出现的间隙问题。希望这对你有所帮助!

英文:

I want to have a button that is always at the bottom of the screen, when the keyboard opens it should go up with it and when it closes the button should go down again.

I tried different ways, with a Box wrapping everything and different configurations of nested Columns.

But sometimes after closing and opening the keyboard there is a gap between the keyboard and the button.

How do I fix that?

This is my current code:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun PlayerNameSelectionScreenContent(
    playerName: TextFieldValue,
    onChangePlayerName: (TextFieldValue) -> Unit = {},
    onContinueButtonClick: () -> Unit = {},
    navigateToNextScreen: () -> Unit = {},
    validateInput: () -> Boolean = { false },
    isInError: Boolean = false,
    errorMessage: String = ""
) {
    val focusManager = LocalFocusManager.current
    val focusRequester = remember { FocusRequester() }
    val hapticGenerator = LocalHapticFeedback.current
    val screenDescription = "Player Name Selection Screen"
    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .semantics { contentDescription = screenDescription },
        verticalArrangement = Arrangement.SpaceBetween,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Column(
            modifier = Modifier
                .weight(0.40f, true)
                .fillMaxWidth(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Spacer(modifier = Modifier.size(32.dp))
            Text(
                text = "Player Setup",
                style = MaterialTheme.typography.displayMedium,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.size(16.dp))
            Text(
                text = "Player 1",
                style = MaterialTheme.typography.headlineLarge,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.size(8.dp))
            Text(
                text = "Start with yourself and proceed in storm order.",
                style = MaterialTheme.typography.bodyLarge,
                textAlign = TextAlign.Center
            )
        }

        Column(
            modifier = Modifier
                .weight(0.40f)
                .fillMaxWidth(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Spacer(modifier = Modifier.size(64.dp))
            Text(
                text = "Set the player name:",
                style = MaterialTheme.typography.headlineSmall,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.size(16.dp))
            val textFieldDescription = "Player name text field"
            OutlinedTextField(
                modifier = Modifier
                    .focusRequester(focusRequester)
                    .semantics {
                        contentDescription = textFieldDescription
                        setText { text ->
                            onChangePlayerName(TextFieldValue(text.text))
                            true
                        }
                        testTag = "PlayerName"
                        testTagsAsResourceId = true
                    },
                value = playerName,
                onValueChange = onChangePlayerName,
                label = { Text(stringResource(id = R.string.name_text)) },
                placeholder = { Text(stringResource(id = R.string.name_text)) },
                singleLine = true,
                isError = isInError,
                keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
                keyboardOptions = KeyboardOptions.Default.copy(
                    imeAction = ImeAction.Done,
                    keyboardType = KeyboardType.Text
                )
            )
            Spacer(modifier = Modifier.size(8.dp))
            val errorMessageDescription =
                stringResource(id = R.string.error_message_description)
            Crossfade(targetState = isInError, label = "isInErrorText") { isInError ->
                Text(
                    modifier = Modifier.semantics {
                        contentDescription = errorMessageDescription
                    },
                    text = if (isInError) errorMessage else "",
                    style = MaterialTheme.typography.bodyLarge,
                    textAlign = TextAlign.Center,
                    color = Color.Red,
                    minLines = 2
                )
            }
        }

        Column(
            modifier = Modifier
                .weight(weight = 0.20f, fill = true)
                .fillMaxWidth(),
            verticalArrangement = Arrangement.Bottom
        ) {
            Crossfade(targetState = isInError, label = "isInErrorButton") { isInError ->
                Button(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(48.dp),
                    shape = RectangleShape,
                    colors = if (isInError) {
                        ButtonDefaults.buttonColors(containerColor = Color.Red)
                    } else {
                        ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary)
                    },
                    onClick = {
                        hapticGenerator.performHapticFeedback(HapticFeedbackType.LongPress)
                        if (validateInput()) {
                            focusManager.clearFocus()
                            onContinueButtonClick()
                            navigateToNextScreen()
                        }
                    }
                ) {
                    Text(
                        modifier = Modifier.padding(horizontal = 16.dp),
                        text = "Continue",
                        style = MaterialTheme.typography.bodyLarge
                    )
                }
            }
        }
    }
    LaunchedEffect(key1 = Unit) {
        focusRequester.requestFocus()
        onChangePlayerName(TextFieldValue(playerName.text, TextRange(playerName.text.length)))
    }
}

Here is how it looks:
Screen Screenshot

And here is a gif that shows what happens if I open and close the keyboard:
Open Keyboard Gif

I tried wrapping everything with a box that has the modifier fillMaxSize and then a Column in it with Arrangement.Bottom in that configuration the gap between the keyboard and button could appear.

Then I tried a few different configurations of the mentioned code with different nestings of the Columns.

I also tried adding or removing the imePadding modifier on the different Boxes or Columns.

答案1

得分: 0

我已经尝试了一些并且认为我找到了一个解决方案:

  1. 我已经在AndroidManifest.xml文件的activity部分中添加了android:windowSoftInputMode="adjustResize"。
<activity
    android:name="..."
    android:windowSoftInputMode="adjustResize"
    ... 
</activity>
  1. 我有三列,我在前两列中添加了修饰符".weight(0.50f)",并从最后一列中移除了weight修饰符。
英文:

I have tried a bit more and think I found a solution:

  1. I have added android:windowSoftInputMode="adjustResize" to the activity section in the AndroidManifest.xml.
&lt;activity
    android:name=&quot;...&quot;
    android:windowSoftInputMode=&quot;adjustResize&quot;
    ... 
&lt;/activity&gt;
  1. I have three columns, I added the modifier .weight(0.50f) to the first two columns and removed the weight modifier from the last column.

huangapple
  • 本文由 发表于 2023年6月11日 22:44:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76451017.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定