处理用户输入

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 TextFieldOutlinedTextField,建议您使用 TextField 而不是 BasicTextField。但是,在构建无需 Material 规范中的装饰的设计时,应使用 BasicTextField

使用 Brush API 设置输入样式

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

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

使用 TextStyle 实现彩色渐变

如需在 TextField 中输入内容时实现彩色渐变,请将所选画笔设置为 TextFieldTextStyle。在此示例中,我们将内置画笔与 linearGradient 结合使用,以便在向 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.使用 buildAnnotatedStringSpanStylelinearGradient 仅自定义一段文本。

设置键盘选项

借助 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 是正确的做法。如果没有,您可以使用可组合项或状态持有器类作为可信来源。如需详细了解在何处提升状态,请参阅状态提升文档。