处理用户输入

TextField 可让用户输入和修改文本。本页介绍了如何实现 TextField、设置 TextField 输入的样式,以及如何配置其他 TextField 选项,例如键盘选项和直观地转换用户输入。

选择“TextField”实现方式

TextField 实现分为两个级别:

  1. TextField 是 Material Design 实现。我们建议您选择此实现,因为它遵循 Material Design 准则
    • 默认样式为填充
    • OutlinedTextField轮廓样式版本
  2. BasicTextField 可让用户通过硬件或软件键盘修改文本,但不提供提示或占位符等装饰。

@Composable
fun SimpleFilledTextFieldSample() {
    var text by remember { mutableStateOf("Hello") }

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

包含 Word 的可编辑文本字段

@Composable
fun SimpleOutlinedTextFieldSample() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

可编辑的文字字段,带有紫色边框和标签。

样式 TextField

TextFieldBasicTextField 共用许多可对它们进行自定义的常用参数。TextField 源代码中提供了 TextField 的完整列表。以下列出了部分有用的参数,但并非详尽无遗:

  • singleLine
  • maxLines
  • textStyle

@Composable
fun StyledTextField() {
    var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

    TextField(
        value = value,
        onValueChange = { value = it },
        label = { Text("Enter text") },
        maxLines = 2,
        textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
        modifier = Modifier.padding(20.dp)
    )
}

多行 TextField,包括两个可编辑的行以及标签

如果您的设计调用 Material TextFieldOutlineTextField,我们建议使用 TextField,而不是 BasicTextField。不过,在构建不需要 Material 规范中的装饰的设计时,应使用 BasicTextField

使用 Brush API 设置输入样式

您可以使用 Brush APITextField 中进行更高级的样式设置。以下部分介绍了如何使用 Brush 为 TextField 输入添加彩色渐变。

如需详细了解如何使用 Brush API 设置文本样式,请参阅使用 Brush API 启用高级样式设置

使用 TextStyle 实现彩色渐变

如需在 TextField 中输入内容时实现彩色渐变,请将所选笔刷设置为 TextFieldTextStyle。在此示例中,我们使用具有 linearGradient 的内置 Brush 查看在 TextField 中输入文本时的彩虹渐变效果。

var text by remember { mutableStateOf("") }
val brush = remember {
    Brush.linearGradient(
        colors = rainbowColors
    )
}
TextField(
    value = text, onValueChange = { text = it }, textStyle = TextStyle(brush = brush)
)

使用 buildAnnotatedString 和 SpanStyle 以及 LinearGradient 仅自定义一段文本。
图 3.使用 buildAnnotatedStringSpanStyle 以及 linearGradient,仅自定义一段文本。

设置键盘选项

借助 TextField,您可以设置键盘配置选项(例如键盘布局),或启用自动更正(如果键盘支持的话)。如果软件键盘不符合此处提供的选项,则无法保证某些选项的可用性。下面列出了支持的键盘选项

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

设置输入格式

TextField 允许您为输入值设置 VisualTransformation,例如将密码中的字符替换为 *,或在信用卡号码中每 4 位插入一个连字符:

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

密码文字输入字段,其中文字被遮盖

如需查看更多示例,请参阅 VisualTransformationSamples 源代码

清除输入

在修改文字时,一项常见的任务是删除前导字符,或者在每次发生更改时转换输入字符串。

因此,您应假设键盘可以对每个 onValueChange 随意进行大量修改。例如,如果用户使用自动更正功能,将某个字词替换为表情符号或使用其他智能编辑功能,就可能会发生这种情况。为了正确处理这种情况,请在编写任何转换逻辑时假设传递到 onValueChange 的当前文本与将传递给 onValueChange 的上一个或下一个值无关。

如需实现不允许使用前导零的文本字段,您可以通过在每次更改值时去除所有前导零来实现此目的。

@Composable
fun NoLeadingZeroes() {
    var input by rememberSaveable { mutableStateOf("") }
    TextField(
        value = input,
        onValueChange = { newText ->
            input = newText.trimStart { it == '0' }
        }
    )
}

如需在清理文本时控制光标位置,请使用 TextFieldTextFieldValue 重载作为状态的一部分。

有关状态的最佳实践

以下是定义和更新 TextField 状态以防止您的应用出现输入问题的一系列最佳实践。

  • 使用 MutableState 表示 TextField 状态:避免使用 StateFlow 等响应式流来表示 TextField 状态,因为这些结构可能会引入异步延迟。

class SignUpViewModel : ViewModel() {

    var username by mutableStateOf("")
        private set

    /* ... */
}

  • 避免延迟更新状态:调用 onValueChange 时,应立即同步更新 TextField

// SignUpViewModel.kt

class SignUpViewModel(private val userRepository: UserRepository) : ViewModel() {

    var username by mutableStateOf("")
        private set

    fun updateUsername(input: String) {
        username = input
    }
}

// SignUpScreen.kt

@Composable
fun SignUpScreen(/*...*/) {

    OutlinedTextField(
        value = viewModel.username,
        onValueChange = { username -> viewModel.updateUsername(username) }
        /*...*/
    )
}

  • 在何处定义状态:如果 TextField 状态需要您在输入时进行业务逻辑验证,那么将状态提升到 ViewModel 是正确的。如果不包含,则可以使用可组合项或状态容器类作为可信来源。如需详细了解在何处提升状态,请参阅状态提升文档。