【问题标题】:Showing a text field in the app bar in Jetpack Compose with Material3在 Jetpack Compose with Material3 的应用栏中显示文本字段
【发布时间】:2022-10-21 01:59:46
【问题描述】:

许多 Android 应用程序在应用程序栏/工具栏中都有一个搜索框,看起来有点像这个屏幕截图:

如何在 Jetpack Compose + Material3 中重新创建类似的东西?我希望文本字段具有以下功能:

  • 支持在内容字符串为空时显示提示(例如“键入您的搜索...”)
  • 自动对焦第一个构图,这样键盘就打开了
  • 将光标放在第一个合成的内容字符串的末尾

我试图在 Jetpack Compose + Material3 中重现相同的行为,方法是将 TextField 放入 titleCenterAlignedTopAppBar 中,但结果看起来不太好。文本字段使用了应用栏的整个高度,并具有灰色背景,这两个东西看起来都很奇怪。

【问题讨论】:

  • 你没有包括截图
  • 显然<img> 标签不起作用,即使它们应该...另外,我已经知道这个问题的答案,我会尽快发布,但由于某种原因,没有向我显示“回答你自己的问题”按钮编译问题(它应该有,根据stackoverflow.com/help/self-answer)。

标签: android android-jetpack-compose android-jetpack-compose-material3 material3


【解决方案1】:

经过一些工程,我想出了一个AppBarTextField,请参见下面的代码。我不得不使用较低级别的BasicTextField,因为普通的TextField 不够可定制。与主题和颜色有关的代码直接从TextField 的实现中复制而来,因此主题的自定义通常适用于文本字段的组件。

AppBarTextField 可组合接受的参数是:

  • value:要在文本字段中显示的内容字符串
  • onValueChange:这里传递了新值(记得更新value!)
  • hint:文本字段为空时显示的提示
  • modifierkeyboardOptionskeyboardActions:它们直接传递给 BasicTextField,它们的行为与普通 TextField 中的行为相同。如果您需要自定义其他TextField 参数,只需将它们添加到函数签名中,然后将它们传递给BasicTextField

实现了请求的功能:

  • 焦点获取是用SideEffect 实现的,所以它只会发生在第一个组合上
  • 光标在末尾在使用TextFieldValue 所需的第一个组合上
  • 奇怪的背景不再存在,因为不存在 .background() 修饰符(虽然它在正常的 TextField 中)
  • 暗示通过在decorationBox 参数中将placeholder 传递给TextFieldDecorationBox 来添加(请注意,这也可以使用TextField
  • TextFieldDecorationBox 的填充现在也只有 4dp。在这里添加了填充(而不是在BasicTextField 上使用修饰符),否则底线指标(而是使用.indicatorLine() 修饰符显示)将无法正确显示。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppBarTextField(
    value: String,
    onValueChange: (String) -> Unit,
    hint: String,
    modifier: Modifier = Modifier,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val textStyle = LocalTextStyle.current
    // make sure there is no background color in the decoration box
    val colors = TextFieldDefaults.textFieldColors(containerColor = Color.Unspecified)

    // If color is not provided via the text style, use content color as a default
    val textColor = textStyle.color.takeOrElse {
        MaterialTheme.colorScheme.onSurface
    }
    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor, lineHeight = 50.sp))

    // request focus when this composable is first initialized
    val focusRequester = FocusRequester()
    SideEffect {
        focusRequester.requestFocus()
    }

    // set the correct cursor position when this composable is first initialized
    var textFieldValue by remember {
        mutableStateOf(TextFieldValue(value, TextRange(value.length)))
    }
    textFieldValue = textFieldValue.copy(text = value) // make sure to keep the value updated

    CompositionLocalProvider(
        LocalTextSelectionColors provides LocalTextSelectionColors.current
    ) {
        BasicTextField(
            value = textFieldValue,
            onValueChange = {
                textFieldValue = it
                // remove newlines to avoid strange layout issues, and also because singleLine=true
                onValueChange(it.text.replace("
", ""))
            },
            modifier = modifier
                .fillMaxWidth()
                .heightIn(32.dp)
                .indicatorLine(
                    enabled = true,
                    isError = false,
                    interactionSource = interactionSource,
                    colors = colors
                )
                .focusRequester(focusRequester),
            textStyle = mergedTextStyle,
            cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
            keyboardOptions = keyboardOptions,
            keyboardActions = keyboardActions,
            interactionSource = interactionSource,
            singleLine = true,
            decorationBox = { innerTextField ->
                // places text field with placeholder and appropriate bottom padding
                TextFieldDefaults.TextFieldDecorationBox(
                    value = value,
                    visualTransformation = VisualTransformation.None,
                    innerTextField = innerTextField,
                    placeholder = { Text(text = hint) },
                    singleLine = true,
                    enabled = true,
                    isError = false,
                    interactionSource = interactionSource,
                    colors = colors,
                    contentPadding = PaddingValues(bottom = 4.dp)
                )
            }
        )
    }
}

这是一个示例用法:

var value by rememberSaveable { mutableStateOf("initial content") }
CenterAlignedTopAppBar(
    title = {
        AppBarTextField(
            value = value,
            onValueChange = { newValue -> value = newValue },
            hint = "A hint..."
        )
    },
    navigationIcon = /* the back icon */,
    actions = /* the search icon */
)

【讨论】:

    猜你喜欢
    • 2021-12-15
    • 2022-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-27
    • 1970-01-01
    • 2022-10-05
    • 2021-11-01
    相关资源
    最近更新 更多