HOME> 职业攻略> 管理文本字段状态

管理文本字段状态

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

基于状态的文本字段

基于值的文本字段

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

实验性: 基于状态的文本字段依赖于 Material 3 版本 1.4.0-alpha14。

请在问题跟踪器中提交任何 bug。

功能

基于值的文本字段

基于状态的文本字段

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

状态管理

使用 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 实现分为两个级别:

TextField 是 Material Design 实现。我们建议您选择此实现,因为它遵循的是 Material Design 指南:

默认样式为填充

OutlinedTextField 是轮廓样式版本

BasicTextField 允许用户使用硬件或软件键盘编辑文本,但没有提供提示或占位符等装饰。

TextField(

state = rememberTextFieldState(initialText = "Hello"),

label = { Text("Label") }

)StateBasedText.kt

OutlinedTextField(

state = rememberTextFieldState(),

label = { Text("Label") }

)StateBasedText.kt

样式 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)

)StateBasedText.kt

如果您的设计调用 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)

)StateBasedText.kt

图 1. TextField内容的彩虹渐变效果。

管理文本字段状态

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

initialText:TextField 的内容。

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

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

注意: 虽然 TextFieldState 属于 Compose Foundation 模块,但它没有界面依赖项,旨在成为强大的状态持有者。TextFieldState 仅使用 Compose 的快照系统提供的数据结构。建议在 ViewModel 中实例化并持有 TextFieldState 实例。

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

val usernameState = rememberTextFieldState()

TextField(

state = usernameState,

lineLimits = TextFieldLineLimits.SingleLine,

placeholder = { Text("Enter Username") }

)StateBasedText.kt

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

TextField(

state = rememberTextFieldState(initialText = "Username"),

lineLimits = TextFieldLineLimits.SingleLine,

)StateBasedText.kt

图 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, "-")

}

)StateBasedText.kt

修改 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)StateBasedText.kt

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

textFieldState.setTextAndPlaceCursorAtEnd("I really love Android")

// textFieldState.text : I really love Android

// textFieldState.selection : TextRange(21, 21)StateBasedText.kt

clearText:清除所有文本。

textFieldState.clearText()

// textFieldState.text :

// textFieldState.selection : TextRange(0, 0)StateBasedText.kt

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

修改用户输入

以下部分介绍了如何修改用户输入。

借助输入转换,您可以在用户输入时过滤 TextField 输入,而输出转换则会在用户输入显示在屏幕上之前对其进行格式设置。

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

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

注意: 请勿在 InputTransformation.transformInput() 调用中更改状态。请改用接收器范围 TextFieldBuffer 来修改文本。系统内置了适用于常见 InputTransformation 用例的过滤条件。如需限制长度,请调用 InputTransformation.maxLength():

TextField(

state = rememberTextFieldState(),

lineLimits = TextFieldLineLimits.SingleLine,

inputTransformation = InputTransformation.maxLength(10)

)StateBasedText.kt

自定义输入转换

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

class CustomInputTransformation : InputTransformation {

override fun TextFieldBuffer.transformInput() {

}

}StateBasedText.kt

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

class DigitOnlyInputTransformation : InputTransformation {

override fun TextFieldBuffer.transformInput() {

if (!asCharSequence().isDigitsOnly()) {

revertAllChanges()

}

}

}StateBasedText.kt

链式输入转换

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

TextField(

state = rememberTextFieldState(),

inputTransformation = InputTransformation.maxLength(6)

.then(CustomInputTransformation()),

)StateBasedText.kt

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

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

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

图 3. 格式正确的美国电话号码和相应的索引。

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

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

class CustomOutputTransformation : OutputTransformation {

override fun TextFieldBuffer.transformOutput() {

}

}StateBasedText.kt

如需设置电话号码的格式,请在 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, "-")

}

}StateBasedText.kt

接下来,将 OutputTransformation 添加到 TextField:

TextField(

state = rememberTextFieldState(),

outputTransformation = PhoneNumberOutputTransformation()

)StateBasedText.kt

转换如何协同运作

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

图 4. 一张图表,显示了文本输入在转换为文本输出之前所经历的转换。

从输入源接收输入。

输入通过 InputTransformation 进行过滤,然后保存到 TextFieldState 中。

输入通过 OutputTransformation 进行格式设置。

输入内容显示在 TextField 中。

设置键盘选项

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

capitalization

autoCorrect

keyboardType

imeAction

其他资源

自动设置文本字段中的电话号码格式

根据用户切换操作显示或隐藏密码

在用户输入时验证输入内容

为您推荐

构建 Compose 界面

状态和 Jetpack Compose

在 Compose 中保存界面状态