配置文本字段

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") }
)

一个可编辑的文本字段,其中包含

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 中实现更高级的样式。 以下部分介绍了如何使用 Brush 为 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,其中显示了文本“Username”。
图 2. TextField,初始文本为“Username”。

使用 TextFieldBuffer 修改文字

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

您经常会在 TextFieldState.edit、InputTransformation.transformInput 或 OutputTransformation.transformOutput 等函数中遇到作为接收器作用域的 TextFieldBuffer。在这些函数中,您可以根据需要读取或更新 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

其他资源