Jetpack Compose价格Visual Transformation首字符0输入崩溃问题求解
解决Jetpack Compose价格Visual Transformation首字符0导致的崩溃问题
你的核心问题是:当输入以0开头(比如"0123"),DecimalFormat会自动移除前导0,导致格式化后的字符串长度短于原输入,进而让偏移量映射数组的索引越界,引发崩溃。以下是无需在onValueChange中转换数字的解决方案:
修改后的实现代码
class PriceTransformation(private val prefix: String = "₹ ") : VisualTransformation { // 定义价格格式化规则:##,##,### private val priceFormatter = DecimalFormat("##,##,###").apply { groupingSize = 3 isParseBigDecimal = true } override fun filter(text: AnnotatedString): TransformedText { val originalText = text.text val originalLength = originalText.length // 处理前导0的特殊情况:如果原文本以0开头且长度>1,格式化时保留逻辑映射 val formattedText = if (originalText.startsWith("0") && originalLength > 1) { // 先格式化去掉前导0后的内容,再把前导0加回去 val trimmedText = originalText.trimStart('0').takeIf { it.isNotEmpty() } ?: "0" prefix + "0" + priceFormatter.format(trimmedText.toBigDecimal()) } else { // 正常情况直接格式化,空输入默认显示₹ 0 prefix + priceFormatter.format(originalText.toBigDecimalOrNull() ?: BigDecimal.ZERO) } val originalToTransformed = mutableListOf<Int>() val transformedToOriginal = mutableListOf<Int>() var specialCharsCount = 0 var originalCharIndex = 0 formattedText.forEachIndexed { transformedIndex, char -> when { // 前缀、空格、逗号属于特殊字符,不对应原文本数字 char == '₹' || char == ' ' || char == ',' -> { specialCharsCount++ transformedToOriginal.add(originalCharIndex) } // 处理手动添加的前导0,映射到原文本的第一个0 formattedText.startsWith(prefix + "0") && transformedIndex == prefix.length -> { originalToTransformed.add(transformedIndex) transformedToOriginal.add(0) originalCharIndex++ } // 普通数字字符,按顺序对应原文本的字符 else -> { originalToTransformed.add(transformedIndex) transformedToOriginal.add(originalCharIndex) originalCharIndex++ } } } // 补充末尾偏移量,避免越界 originalToTransformed.add( originalToTransformed.lastOrNull()?.plus(1) ?: formattedText.length ) transformedToOriginal.add( transformedToOriginal.lastOrNull()?.plus(1) ?: originalLength ) val offsetMapping = object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { // 边界保护:偏移量超出数组长度时返回文本末尾 return if (offset < originalToTransformed.size) { originalToTransformed[offset] } else { formattedText.length } } override fun transformedToOriginal(offset: Int): Int { // 边界保护:偏移量超出数组长度时返回原文本末尾 return if (offset < transformedToOriginal.size) { transformedToOriginal[offset] } else { originalLength } } } return TransformedText( text = AnnotatedString(formattedText), offsetMapping = offsetMapping ) } }
关键改动说明
- 前导0保留逻辑:当原文本以0开头且长度大于1时,先剥离前导0进行格式化,再手动把0加回结果开头,既保证视觉上的前导0显示,又避免DecimalFormat自动移除它导致的长度不匹配。
- 精准偏移映射:专门为手动添加的前导0建立映射关系,将其对应到原文本的第一个字符位置,确保光标移动逻辑正确。
- 边界安全处理:在
OffsetMapping的两个方法中添加边界判断,即使偏移量超出数组范围,也返回合法的文本末尾索引,彻底避免数组越界崩溃。 - 空输入兼容:原文本为空时默认格式化显示
₹ 0,避免转换异常。
内容的提问来源于stack exchange,提问作者sarthak gupta
相关产品推荐
相关产品推荐

