Jetpack Compose美国手机号VisualTransformation光标问题及功能需求
美国手机号实时格式化(Jetpack Compose)
问题场景
需要实现Jetpack Compose输入框的美国手机号实时格式化,规则如下:
- 空输入时显示
+1 - 输入过程自动排版,最终格式为
+1 (123) 456-7890 - 当前已实现基础格式化,但存在两个问题:空输入时光标位置错误;无法在字符中间插入数字
修复后的完整实现
1. 手机号格式化VisualTransformation
private val phoneNumberFilter = VisualTransformation { text -> val rawDigits = text.text.takeWhile { it.isDigit() }.take(10) val formatted = buildString { append("+1 ") if (rawDigits.isNotEmpty()) { append("(") append(rawDigits.take(3)) if (rawDigits.length > 3) { append(") ") append(rawDigits.substring(3, 6)) if (rawDigits.length > 6) { append("-") append(rawDigits.substring(6)) } } } } val mapping = object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { return when (offset) { 0 -> 3 // 空输入或光标在最前时,定位到+1后面的空格后 in 1..3 -> offset + 3 // 前3位数字对应格式化后的位置:+1 (X... in 4..6 -> offset + 4 // 中间3位对应:+1 (XXX) X... else -> offset + 5 // 最后4位对应:+1 (XXX) XXX-... } } override fun transformedToOriginal(offset: Int): Int { return when (offset) { in 0..3 -> 0 // 点击+1区域,光标回到原始最前 in 4..6 -> offset - 3 // 对应前3位原始位置 in 7..9 -> offset - 4 // 对应中间3位原始位置 else -> offset - 5 // 对应最后4位原始位置 }.coerceIn(0, rawDigits.length) } } TransformedText(AnnotatedString(formatted), mapping) }
2. 输入框UI代码
var phone by remember { mutableStateOf("") } OutlinedTextField( modifier = Modifier.fillMaxWidth(), value = phone, onValueChange = { input -> val digits = input.takeWhile { it.isDigit() }.take(10) phone = digits }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), visualTransformation = phoneNumberFilter, placeholder = { Text(text = "+1") } )
修复关键点说明
- 空输入光标修正:在
originalToTransformed中,当原始偏移为0时直接返回3(对应+1之后的位置),解决空输入时光标错位问题;transformedToOriginal处理点击+1区域时,将光标定位到原始输入最前端。 - 中间插入支持:通过分段偏移映射,确保光标在格式化后的任意位置点击时,都能正确映射到原始数字的对应位置,支持在任意位置插入或删除数字。
- 格式化逻辑优化:使用
buildString高效构建格式化文本,按分段处理不同长度的输入,确保逐步排版符合预期。
内容的提问来源于stack exchange,提问作者Arpit Patel
相关产品推荐
相关产品推荐

