You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中TextField结合TransformedText失焦再聚焦崩溃问题

修复Jetpack Compose手机号输入格式化半输入时聚焦崩溃问题

问题分析

崩溃报错java.lang.IllegalStateException: OffsetMapping.transformedToOriginal returned invalid mapping: 15 -> 10 is not in range of original text [0, 5],核心原因是当前OffsetMapping的逻辑是针对完整10位手机号硬编码的,当原始文本长度不足10位时,transformedToOriginal返回的位置超出了原始文本的实际范围。比如输入5位字符时,原始文本长度仅为5,但代码里直接返回固定值10,触发了Compose对偏移映射的合法性校验,导致崩溃。

修复方案

需要根据原始文本的实际长度动态计算偏移映射,同时确保转换后的原始位置始终在[0, 原始文本长度]范围内。修改phoneNumberInputFormatter中的OffsetMapping实现,结合当前输入的文本长度调整逻辑:

修改后的完整格式化器代码

private fun phoneNumberInputFormatter(text: AnnotatedString): TransformedText {
    val mask = "+1 xxx-xxx-xxxx"
    val trimmed = if (text.text.length >= 10) text.text.substring(0..9) else text.text
    val originalLength = trimmed.length

    val annotatedString = AnnotatedString.Builder().run {
        for (i in trimmed.indices) {
            if (i == 0) {
                append("+1 ")
            }
            append(trimmed[i])
            if (i == 2 || i == 5) {
                append("-")
            }
        }
        pushStyle(SpanStyle(color = Color.LightGray))
        append(mask.takeLast(mask.length - length))
        toAnnotatedString()
    }

    val phoneNumberOffsetTranslator = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {
            var transformedOffset = offset
            // 加上固定前缀"+1 "的长度
            if (offset > 0) transformedOffset += 2
            // 第一个分隔符"-",仅当原始偏移超过3位时累加
            if (offset > 3) transformedOffset += 1
            // 第二个分隔符"-",仅当原始偏移超过6位时累加
            if (offset > 6) transformedOffset += 1
            // 限制偏移不超过掩码总长度
            return min(transformedOffset, mask.length)
        }

        override fun transformedToOriginal(offset: Int): Int {
            var originalOffset = offset - 2
            // 减去第一个分隔符的偏移,仅当转换后的位置超过"+1 xxx"区域时生效
            if (offset > 5) originalOffset -= 1
            // 减去第二个分隔符的偏移,仅当转换后的位置超过"+1 xxx-"区域时生效
            if (offset > 9) originalOffset -= 1
            // 强制限制原始偏移在合法范围内,避免越界
            return originalOffset.coerceIn(0, originalLength)
        }
    }
    return TransformedText(annotatedString, phoneNumberOffsetTranslator)
}

关键修复点

  • originalToTransformed:改为根据原始偏移动态累加分隔符长度,避免硬编码判断范围,同时用min限制偏移不超过掩码总长度。
  • transformedToOriginal:
    1. 先处理固定前缀"+1 "的偏移
    2. 仅当转换后的位置超过对应分隔符的实际位置时,才减去分隔符的偏移量
    3. 最后用coerceIn强制保证返回的原始位置始终在[0, originalLength]范围内,彻底杜绝越界问题

额外优化

可以在onValueChange中限制输入仅为数字,避免非数字字符干扰格式化逻辑:

onValueChange = { input ->
    num = input.filter { it.isDigit() }
}

内容的提问来源于stack exchange,提问作者Murphler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 02:52:07