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”时,附加了逗号,突然键盘的数字视图再次变为字母。
这里我修改了selectionof ,TextFieldValue以便每当附加逗号时光标始终位于消息的末尾。
这种情况正是我们的VisualTransformation目的。
我认为我们不能轻易解决这个问题。
一般不建议在 onValueChanged 回调中过滤文本,因为文本状态与进程外 IME(软件键盘)共享。过滤文本是指文本内容在内部发生变化,然后将新的状态通知给IME。这不是 IME 的正常路径,不同的 IME 对这种意外状态变化的反应也不同。有些IME可能会尝试重建组合,其他IME可能会放弃并开始新的会话等。这主要是由于历史原因,从现在起很难修复。因此,请避免在 onValueChanged 回调中过滤文本并考虑以下替代方案:
- (推荐)不要过滤它并显示错误消息。(此处无关)
- 使用 VisualTransformation 更改视觉输出而不修改编辑缓冲区。
根据上面提到的答案,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)