Android Jetpack Compose:修改输入文本后键盘从数字更改为字母

Shr*_*til 8 user-interface android textfield android-layout android-jetpack-compose

在探索TextFieldJetpack Compose 时,我遇到了一种情况,我必须修改在字段中键入的输入。例如,输入3个字符后添加逗号

我就是这样做的。

@Composable
fun TFDemo() {
    var fieldValue by remember { mutableStateOf(TextFieldValue("")) }

    TextField(
        value = fieldValue,
        onValueChange = {
            val newMessage = it.text.let { text -> if (text.length == 3) "$text," else text }
            fieldValue = it.copy(newMessage, selection = TextRange(newMessage.length))
        },
        keyboardOptions = KeyboardOptions(autoCorrect = false),
    )
}
Run Code Online (Sandbox Code Playgroud)

但运行后,我意识到添加逗号后,键盘视图从数字/符号变回字母,但事实不应该如此。为清楚起见,请参阅下面的视频输出

正如您在下面的视频中看到的,当我输入“111”时,附加了逗号,突然键盘的数字视图再次变为字母。

Jetpack Compose:键盘更改问题


这里我修改了selectionof ,TextFieldValue以便每当附加逗号时光标始终位于消息的末尾。

H.D*_*.D. 6

这种情况正是我们的VisualTransformation目的。

以下是 Google 员工对另一个问题的评论:

我认为我们不能轻易解决这个问题。

一般不建议在 onValueChanged 回调中过滤文本,因为文本状态与进程外 IME(软件键盘)共享。过滤文本是指文本内容在内部发生变化,然后将新的状态通知给IME。这不是 IME 的正常路径,不同的 IME 对这种意外状态变化的反应也不同。有些IME可能会尝试重建组合,其他IME可能会放弃并开始新的会话等。这主要是由于历史原因,从现在起很难修复。因此,请避免在 onValueChanged 回调中过滤文本并考虑以下替代方案:

  1. (推荐)不要过滤它并显示错误消息。(此处无关)
  2. 使用 VisualTransformation 更改视觉输出而不修改编辑缓冲区。


Shr*_*til 3

根据上面提到的答案,VisualTransformation这是这种情况的完美解决方案,我们不应该直接修改 TextField 的缓冲区。因为VisualTransformation只是改变文本的视觉输出,而不是实际文本。

我在这里写了一篇关于这种情况的文章,详细解释了这一点。

解决方案:

@Composable
fun TextFieldDemo() {
    var message by remember { mutableStateOf("") }

    TextField(
        value = message,
        placeholder = { Text("Enter amount or message") },
        onValueChange = { message = it },
        visualTransformation = AmountOrMessageVisualTransformation()
    )
}

class AmountOrMessageVisualTransformation : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {

        val originalText = text.text
        val formattedText = formatAmountOrMessage(text.text)

        val offsetMapping = object : OffsetMapping {

            override fun originalToTransformed(offset: Int): Int {
                if (originalText.isValidFormattableAmount) {
                    val commas = formattedText.count { it == ',' }
                    return when {
                        offset <= 1 -> offset
                        offset <= 3 -> if (commas >= 1) offset + 1 else offset
                        offset <= 5 -> if (commas == 2) offset + 2 else offset + 1
                        else -> 8
                    }
                }
                return offset
            }

            override fun transformedToOriginal(offset: Int): Int {
                if (originalText.isValidFormattableAmount) {
                    val commas = formattedText.count { it == ',' }
                    return when (offset) {
                        8, 7 -> offset - 2
                        6 -> if (commas == 1) 5 else 4
                        5 -> if (commas == 1) 4 else if (commas == 2) 3 else offset
                        4, 3 -> if (commas >= 1) offset - 1 else offset
                        2 -> if (commas == 2) 1 else offset
                        else -> offset
                    }
                }
                return offset
            }
        }

        return TransformedText(
            text = AnnotatedString(formattedText),
            offsetMapping = offsetMapping
        )
    }
}
Run Code Online (Sandbox Code Playgroud)