配置文本字段

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

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

功能

基于值的文本字段

基于状态的文本字段

基于状态的优势

状态管理

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

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

  • onValueChange 回调已被移除,这可以防止您引入异步行为。
  • 状态在重组、配置和进程终止后仍然存在。

视觉转换

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

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

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

行限制

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

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

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

安全文本字段

不适用

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

  • 让您可以在后台优化安全性,并附带一个带有 textObfuscationMode 的预定义 UI。

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

选择 TextField 实现

TextField 实现有两级

  1. TextField 是 Material Design 实现。我们建议您选择此实现,因为它遵循 Material Design 指南
  2. BasicTextField 允许用户通过硬件或软件键盘编辑文本,但不提供提示或占位符等修饰。

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

An editable text field containing the word

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

An editable text field, with a purple border and 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)
)

A multiline TextField, with two editable lines plus the label

当您的设计需要 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)
)

Using buildAnnotatedString and SpanStyle, along with linearGradient, to customize only a piece of text.
图 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,
)

A TextField with the text Username appearing inside the text field.
图 2. 初始文本为“Username”的 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 中。以上一个电话号码示例为例,您需要在适当位置添加括号和破折号

An American phone number, properly formatted with parentheses, dashes, and corresponding indices.
图 3. 格式正确的美国电话号码和对应的索引。

这是处理基于值的 TextFieldVisualTransformation 的更新方法,主要区别在于您无需计算其偏移映射。

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()
)

转换如何协同工作

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

A visualization of how text input goes through transformations before it becomes text output.
图 4. 显示文本输入如何通过转换才成为文本输出的图表。
  1. 从输入源接收输入。
  2. 输入通过 InputTransformation 进行过滤,并保存在 TextFieldState 中。
  3. 输入通过 OutputTransformation 进行格式化。
  4. 输入呈现在 TextField 中。

设置键盘选项

TextField 允许您设置键盘配置选项,例如键盘布局,或在键盘支持的情况下启用自动更正。如果软件键盘不符合此处提供的选项,某些选项可能无法保证。以下是支持的键盘选项列表

  • capitalization(大小写)
  • autoCorrect(自动更正)
  • keyboardType(键盘类型)
  • imeAction(IME 操作)

其他资源