配置文本字段

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

我们建议您使用基于状态的文本字段,因为它们提供了一种更完整、更可靠的方法来管理 TextField 的状态。下表概述了这些类型的文本字段之间的差异,并包含基于状态的文本字段提供的主要优势:

功能

基于值的文本字段

基于状态的文本字段

基于州/省/自治区/直辖市的福利

状态管理

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

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

  • 移除了 onValueChange 回调,这会阻止您引入异步行为。
  • 该状态在重组、配置和进程终止后仍然有效。

视觉转换

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

使用 InputTransformation 在用户输入提交到状态之前修改该输入,并使用 OutputTransformation 设置文本字段内容的格式,而不会更改底层状态数据。

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

行数限制

接受 singleLine: Boolean, maxLines: IntminLines: Int 以控制行数。

使用 lineLimits: TextFieldLineLimits 配置文本字段可占用的行数下限和上限。

  • 通过提供类型为 TextFieldLineLimitslineLimits 参数,消除了配置行限制时的模糊性。

安全文本字段

不适用

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

TextFieldBasicTextField 共用许多可用于自定义的常用参数。如需查看 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 TextFieldOutlinedTextField,建议您使用 TextField 而不是 BasicTextField。但是,在构建无需 Material 规范中的装饰的设计时,应使用 BasicTextField

使用 Brush API 设置输入样式

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

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

使用 TextStyle 实现彩色渐变

如需在 TextField 中输入内容时实现彩色渐变,请将所选画笔设置为 TextFieldTextStyle。在此示例中,我们将内置画笔与 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)
)

使用 buildAnnotatedString 和 SpanStyle 以及 linearGradient 仅自定义一段文本。
图 1. TextField 内容的彩虹渐变效果。

管理文本字段状态

TextField 使用名为 TextFieldState 的专用状态容器类来存储其内容和当前选择。TextFieldState 旨在根据架构中的适用位置进行提升。TextFieldState 提供 2 个主要属性:

  • initialTextTextField 的内容。
  • 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 类似。它既包含文本内容,也包含当前所选内容的相关信息。

您经常会在 TextFieldState.editInputTransformation.transformInputOutputTransformation.transformOutput 等函数中遇到 TextFieldBuffer 作为接收器作用域。在这些函数中,您可以根据需要读取或更新 TextFieldBuffer。之后,这些更改会提交到 TextFieldState,或者在 OutputTransformation 的情况下传递给渲染流水线。

您可以使用标准编辑函数(例如 appendinsertreplacedelete)修改缓冲区的内容。如需更改选择状态,请直接设置其 selection: TextRange 变量,或使用 placeCursorAtEndselectAll 等实用函数。选择本身由 TextRange 表示,其中开始索引包含边界值,结束索引不含边界值。具有相同开始值和结束值的 TextRange(例如 (3, 3))表示当前没有选中任何字符的光标位置。

val phoneNumberState = rememberTextFieldState()

LaunchedEffect(phoneNumberState) {
    phoneNumberState.edit { // TextFieldBuffer scope
        append("123456789")
    }
}

TextField(
    state = phoneNumberState,
    inputTransformation = InputTransformation { // TextFieldBuffer scope
        if (asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

修改 TextFieldState 中的文本

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

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

    val usernameState = rememberTextFieldState("I love Android")
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    usernameState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    usernameState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

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

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

  • clearText:清除所有文本。

    usernameState.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 (!TextUtils.isDigitsOnly(asCharSequence())) {
            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

其他资源