经过一些工程,我想出了一个AppBarTextField,请参见下面的代码。我不得不使用较低级别的BasicTextField,因为普通的TextField 不够可定制。与主题和颜色有关的代码直接从TextField 的实现中复制而来,因此主题的自定义通常适用于文本字段的组件。
AppBarTextField 可组合接受的参数是:
-
value:要在文本字段中显示的内容字符串
-
onValueChange:这里传递了新值(记得更新value!)
-
hint:文本字段为空时显示的提示
-
modifier、keyboardOptions 和 keyboardActions:它们直接传递给 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 */
)