如何在Basictextfield jetpack compose中使用视觉转换

问题描述 投票:0回答:1

我想对 Jetpack Compose 中的 BasicTextField 应用视觉转换。我尝试了一些代码,但遇到了几个问题。例如,光标没有停留在正确的位置,按退格键时,光标移动到错误的位置。

我需要 BasicTextField 始终显示无法删除的特定字符串(例如“ABC-”),即使使用退格键或清除操作也是如此。此外,我想限制输入并应用屏蔽来格式化文本,如下所示:“ABC-1234-2345-6366”。

有人可以帮助我正确地实现这个吗?

@Preview(showBackground = true)
 @Composable
 private fun SampleTextFieldView() {
     var textFieldValue by remember { mutableStateOf(TextFieldValue("ABC-")) }
     BasicFieldView(
         textFieldValue,
         onValueChange = { onValueChange ->
             val digitOnly = onValueChange.text.filter { it.isDigit() }
             val formattedString = buildString {
                 append("Abc-")
                 digitOnly.forEachIndexed { index, digit ->
                     if (index % 4 == 0 && index !=0 ){
                         append("-")
                     }
                     append(digit)
                 }
             }
             textFieldValue = onValueChange.copy(formattedString)
         }
     )
 }
 
 @Composable
 fun BasicFieldView(
     textFieldValue: TextFieldValue,
     onValueChange: (TextFieldValue) -> Unit,
 ) {
     Column(
         modifier = Modifier.fillMaxSize()
     ) {
         BasicTextField(
             modifier = Modifier.fillMaxWidth(),
             value = textFieldValue,
             onValueChange = {
                 onValueChange(it)
             },
         )
     }
 }
android kotlin android-jetpack-compose android-compose-textfield
1个回答
0
投票

首先,您应该使用新的 BasicTextField 重载,它使用

TextFieldState
而不是
TextFieldValue
。这解决了有关监视文本字段值更新的许多问题,并接受
InputTransformation
来修改用户输入的数据,并接受
OutputTransformation
来修改文本字段值的显示方式。

现在,为了满足您的要求,需要以下内容:

  • 要将输入限制为12个字符并确保只能输入数字,您需要InputTransformations。对于最大长度,您可以使用内置

    InputTransformation.maxLength(12)
    。要将输入限制为仅数字,您可以创建自定义输入转换:

    object DigitsOnlyTransformation : InputTransformation {
        override val keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
    
        override fun TextFieldBuffer.transformInput() {
            if (!asCharSequence().isDigitsOnly()) {
                revertAllChanges()
            }
        }
    }
    

    keyboardOptions
    也可以直接在BasicTextField中指定,但我发现将有关仅数字要求的所有限制捆绑在一处会更清晰。
    TextFieldBuffer
    可以根据需要修改。
    revertAllChanges()
    忽略最新更改。这与过滤掉所有非数字的实现有点不同。仅当用户一次输入多个字符时(例如从剪贴板粘贴混合数字和非数字),这才有意义。您可以根据需要进行调整。

    这两个 InputTransformation 可以链接在一起,然后传递到 BasicTextField,如下所示:

    BasicTextField(
        ...
        inputTransformation = InputTransformation.maxLength(12)
            .then(DigitsOnlyTransformation),
    )
    
  • 要为显示提供带有破折号的蒙版,只需创建一个新的OutputTransformation。 OutputTransformations 的工作方式与 InputTransformations 相同,只是它们不会更改文本字段的值,输出仅用于 UI 中的显示:

    @Stable
    data class GroupingOutputTransformation(
        private val groupSize: Int,
        private val groupDelimiter: String,
    ) : OutputTransformation {
        override fun TextFieldBuffer.transformOutput() {
            repeat((length - 1) / groupSize) {
                insert(it + (it + 1) * groupSize, groupDelimiter)
            }
        }
    }
    

    像这样使用它:

    BasicTextField(
        ...
        outputTransformation = GroupingOutputTransformation(4, "-"),
    )
    

    它会在每个

    -
    字符之后插入
    4
    (末尾除外)。

  • 要在文本字段值之前显示不可修改的前缀,您可以使用OutputTransformation(您可以通过在重复循环后添加insert(0, "ABC-")

    来尝试此操作)。虽然乍一看这似乎按预期工作,但光标可以放置在前缀
    之前。尽管输入的所有字符都将插入到前缀之后,但这可能仍然是不受欢迎的,并且可能会让用户感到困惑。尽管有一个可用于 TextFieldBuffer 的 placeCursorAtEnd() 函数可以解决此问题,但它仅在用于 InputTransformation 时有效,而不是在 OutputTransformation 中使用。
    另一种方法是显示文本字段的前缀 
    outside

    。这可以通过使用 BasicTextField 的

    decorator 参数轻松完成:

    BasicTextField(
        ...
        decorator = { innerTextField ->
            Row {
                Text(text = "ABC-", style = TextStyle.Default)
                innerTextField()
            }
        },
    )
    

    innerTextField
    是您可以根据需要布局的实际文本字段,在本例中位于行中,前面放置了前缀。您需要将相同的 TextStyle 应用于为 BasicTextField 设置的文本(其中使用默认的

    TextStyle.Default

    ),以便它无缝地混合在一起。
    
    

  • 将所有内容放在一起,您的 BasicFieldView 将如下所示:

@Composable fun BasicFieldView( state: TextFieldState, textStyle: TextStyle = TextStyle.Default, prefix: String = "ABC-", maxLength: Int = 12, groupSize: Int = 4, groupDelimiter: String = "-", ) { Column( modifier = Modifier.fillMaxSize(), ) { BasicTextField( state = state, modifier = Modifier.fillMaxWidth(), inputTransformation = InputTransformation.maxLength(maxLength) .then(DigitsOnlyTransformation), textStyle = textStyle, outputTransformation = GroupingOutputTransformation(groupSize, groupDelimiter), decorator = { innerTextField -> Row { Text(text = prefix, style = textStyle) innerTextField() } }, ) } }

常量被提取为具有默认值的参数,以便您可以在需要时轻松调整它们。
如您所见,

textFieldValue

onValueChange

参数现在已替换为
state
。正如开头提到的,TextFieldState 是现在使用 TextFields 的现代方式(从即将推出的 1.4.0 版本的 
TextField
 开始,它也可用于样式化的 Material 3 
OutlinedTextField
androidx.compose.material3:material3
)。使用 TextFieldState,您不再有 onValueChange 回调,该回调会在内部自动处理。
只需将 
rememberTextFieldState()

传递给 BasicFieldView 即可。将其保存在变量中并观察其

text

 属性是否发生变化。它是一个 MutableState,因此无论在何处使用,更改都会自动触发重组。或者,您可以在视图模型中使用 
val state = TextFieldState()
。如果您想在值更改时触发某些操作,请将其包装在 snapshotFlow 中:
snapshotFlow { state.text }

现在可以在 Compose 外部或 LaunchedEffect 内部收集结果流。
    

© www.soinside.com 2019 - 2024. All rights reserved.