配置文本字段

TextField 允许用户输入和修改文字。您可以使用两种类型的文本字段:基于状态的文本字段和基于值的文本字段。选择您要显示哪种类型的内容:

我们建议使用基于状态的文本字段,因为它们提供了一种更完整、更可靠的方法来管理 TextField 的状态。下表列出了这些类型的文本字段之间的区别,并介绍了基于状态的文本字段的主要优势:

功能

基于值的文本字段

基于状态的文本字段

基于状态的福利

状态管理

使用 onValueChange 回调更新文本字段状态。您有责任根据 onValueChange 报告的更改更新您自己状态中的 value。

明确使用 TextFieldState 对象来管理文本输入状态(值、选择和撰写)。此状态可以记住并共享。

  • 移除了 onValueChange 回调,这可防止您引入异步行为。
  • 该状态在重组、配置和进程终止后继续存在。

可视化转换

使用 VisualTransformation 修改显示文本的显示方式。这通常会在一个步骤中同时处理输入和输出格式。

使用 InputTransformation 在用户输入提交到状态之前对其进行修改,并使用 OutputTransformation 格式化文本字段内容,而无需更改底层状态数据。

  • 您不再需要使用 OutputTransformation 提供原始纯文本与转换后文本之间的偏移量映射。

行数限制

接受 singleLine: Boolean, maxLines: Int 和 minLines: Int 来控制行数。

使用 lineLimits: TextFieldLineLimits 配置文本字段可占用的最小和最大行数。

  • 通过提供 TextFieldLineLimits 类型的 lineLimits 参数,消除了配置行限制时的歧义。

安全文本字段

不适用

SecureTextField 是基于状态的文本字段构建的可组合项,用于写入密码字段。

  • 可让您在后台优化安全性,并附带预定义的 textObfuscationMode 界面。

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

选择 TextField 实现

TextField 实现分为两个级别:

  1. TextField 是 Material Design 实现。我们建议您选择此实现,因为它遵循的是 Material Design 指南:
    • 默认样式为填充
    • OutlinedTextField 是轮廓样式版本
  2. BasicTextField 允许用户使用硬件或软件键盘编辑文本,但没有提供提示或占位符等装饰。

TextField(
    state = rememberTextFieldState(initialText = "Hello"),
    label = { Text("Label") }
)

包含“Hello”字样的可编辑文字字段。

OutlinedTextField(
    state = rememberTextFieldState(),
    label = { Text("Label") }
)

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

样式 TextField

TextField 和 BasicTextField 共用许多可进行自定义的常用参数。如需查看 TextField 的完整列表,请参阅 TextField 源代码。以下列出了部分有用的参数,但并非详尽无遗:

  • textStyle
  • lineLimits

TextField(
    state = rememberTextFieldState("Hello\nWorld\nInvisible"),
    lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 2),
    placeholder = { Text("") },
    textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
    label = { Text("Enter text") },
    modifier = Modifier.padding(20.dp)
)

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

如果您的设计调用 Material TextField 或 OutlinedTextField,建议您使用 TextField 而不是 BasicTextField。但是,在构建无需 Material 规范中的装饰的设计时,应使用 BasicTextField。

使用 Brush API 设置输入样式

您可以使用 Brush API 在 TextField 中实现更高级的样式设置。 以下部分介绍了如何使用笔刷向 TextField 输入添加彩色渐变。

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

使用 TextStyle 实现彩色渐变

如需在 TextField 中实现彩色渐变效果,请将所选画笔设置为 TextField 的 TextStyle。在此示例中,我们使用具有 linearGradient 的内置画笔,以便在将文字输入到 TextField 中时查看彩虹渐变效果。

val brush = remember {
    Brush.linearGradient(
        colors = listOf(Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Magenta)
    )
}
TextField(
    state = rememberTextFieldState(), textStyle = TextStyle(brush = brush)
)

在文本字段中输入文字,显示彩虹渐变效果。
图 1. TextField内容的彩虹渐变效果。

管理文本字段状态

TextField 使用名为 TextFieldState 的专用状态容器类来管理其内容和选择。TextFieldState 旨在放置在架构中合适的任何位置。TextFieldState 提供 2 个主要属性:

  • initialText:TextField 的内容。
  • initialSelection:指示光标或所选内容的位置。

TextFieldState 与其他方法(例如 onValueChange 回调)的区别在于,TextFieldState 完全封装了整个输入流程。这包括使用正确的后备数据结构、内嵌过滤器和格式化程序,以及同步来自不同来源的所有编辑内容。

您可以使用 TextFieldState() 来提升 TextField 中的状态。为此,我们建议使用 rememberTextFieldState() 函数。rememberTextFieldState() 会在可组合函数中创建 TextFieldState 实例,确保状态对象被记住,并提供内置的保存和恢复功能:

val usernameState = rememberTextFieldState()
TextField(
    state = usernameState,
    lineLimits = TextFieldLineLimits.SingleLine,
    placeholder = { Text("Enter Username") }
)

rememberTextFieldState 可以具有空白参数,也可以传入初始值来表示初始化时的文本值。如果在后续的重组中传入不同的值,状态的值不会更新。如需在初始化后更新状态,请对 TextFieldState 调用编辑方法。

TextField(
    state = rememberTextFieldState(initialText = "Username"),
    lineLimits = TextFieldLineLimits.SingleLine,
)

一个 TextField,其中包含显示在文本字段内的文本“用户名”。
图 2. TextField,并将“用户名”作为初始文本。

使用 TextFieldBuffer 修改文字

TextFieldBuffer 用作可编辑的文本框,功能类似于 StringBuilder。它包含文本内容和有关所选内容的信息。

您经常会遇到 TextFieldBuffer 作为接收器范围出现在 TextFieldState.edit、InputTransformation.transformInput 或 OutputTransformation.transformOutput 等函数中。在这些函数中,您可以根据需要读取或更新 TextFieldBuffer。之后,这些更改会提交到 TextFieldState,如果是 OutputTransformation,则会传递到渲染流水线。

您可以使用 append、insert、replace 或 delete 等标准编辑函数来修改缓冲区的内容。如需更改选择状态,您可以直接设置其 selection: TextRange 变量,也可以使用 placeCursorAtEnd 或 selectAll 等实用函数。选择本身由 TextRange 表示,其中起始索引包含在范围内,而结束索引不包含在范围内。具有相同起始值和结束值的 TextRange(例如 (3, 3))表示光标位置未选择任何字符。

val phoneNumberState = rememberTextFieldState("1234567890")

TextField(
    state = phoneNumberState,
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Phone
    ),
    inputTransformation = InputTransformation.maxLength(10).then {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

修改 TextFieldState 中的文本

您可以通过多种方法直接通过状态变量修改状态:

  • edit:可让您修改状态内容,并提供 TextFieldBuffer 函数,以便您使用 insert、replace、append 等方法。

    // Initial textFieldState text passed in is "I love Android"
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    textFieldState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    textFieldState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

  • setTextAndPlaceCursorAtEnd:清除当前文本,将其替换为指定文本,并将光标设置在末尾。

    textFieldState.setTextAndPlaceCursorAtEnd("I really love Android")
    // textFieldState.text : I really love Android
    // textFieldState.selection : TextRange(21, 21)

  • clearText:清除所有文本。

    textFieldState.clearText()
    // textFieldState.text :
    // textFieldState.selection : TextRange(0, 0)

如需了解其他 TextFieldState 函数,请参阅 TextFieldState 参考文档。

修改用户输入

以下部分介绍了如何修改用户输入。 借助输入转换,您可以在用户输入时过滤 TextField 输入,而输出转换则会在用户输入显示在屏幕上之前对其进行格式设置。

通过输入转换过滤用户输入

借助输入转换,您可以过滤来自用户的输入。例如,如果您的 TextField 接受美国电话号码,您只想接受 10 位数字。InputTransformation 的结果保存在 TextFieldState 中。

系统内置了适用于常见 InputTransformation 用例的过滤条件。如需限制长度,请调用 InputTransformation.maxLength():

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine,
    inputTransformation = InputTransformation.maxLength(10)
)

自定义输入转换

InputTransformation 是一个单一的函数接口。实现自定义 InputTransformation 时,您需要替换 TextFieldBuffer.transformInput:

class CustomInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
    }
}

对于电话号码,请添加一个自定义输入转换,该转换仅允许在 TextField 中输入数字:

class DigitOnlyInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    }
}

链式输入转换

如需对文本输入添加多个过滤条件,请使用 then 扩展函数链接多个 InputTransformation。过滤器按顺序执行。作为最佳实践,请先应用选择性最强的过滤器,以避免对最终会被过滤掉的数据进行不必要的转换。

TextField(
    state = rememberTextFieldState(),
    inputTransformation = InputTransformation.maxLength(6)
        .then(CustomInputTransformation()),
)

添加输入转换后,TextField 输入最多接受 10 位数字。

在显示输入内容之前设置其格式

OutputTransformation 可让您在用户输入内容显示在屏幕上之前对其进行格式设置。与 InputTransformation 不同,通过 OutputTransformation 完成的格式设置不会保存在 TextFieldState 中。在上一个电话号码示例的基础上,您需要在适当的位置添加圆括号和短划线:

一个美国电话号码,格式正确,包含括号、短划线和相应索引。
图 3. 格式正确的美国电话号码和对应的索引。

这是在基于值的 TextField 中处理 VisualTransformation 的更新方式,主要区别在于您不必计算其偏移量映射。

OutputTransformation 是单一抽象方法接口。为了实现自定义 OutputTransformation,您需要替换 transformOutput 方法:

class CustomOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
    }
}

如需设置电话号码的格式,请在 OutputTransformation 中添加以下内容:在索引 0 处添加左圆括号,在索引 4 处添加右圆括号,在索引 8 处添加短划线:

class PhoneNumberOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
}

接下来,将 OutputTransformation 添加到 TextField:

TextField(
    state = rememberTextFieldState(),
    outputTransformation = PhoneNumberOutputTransformation()
)

转换如何协同运作

下图显示了从文本输入到转换再到输出的流程:

直观展示了文本输入在转换为文本输出之前经历的转换。
图 4. 一张图表,显示了文本输入在转换为文本输出之前所经历的转换。
  1. 从输入源接收输入。
  2. 输入通过 InputTransformation 进行过滤,然后保存到 TextFieldState 中。
  3. 输入通过 OutputTransformation 进行格式设置。
  4. 输入内容显示在 TextField 中。

设置键盘选项

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

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

其他资源