EditText实现货币格式化时,如何让光标停在新输入字符后方?
解决EditText货币格式化后光标位置异常问题
你的核心问题是格式化后直接将光标固定在末尾,没有考虑用户实际输入/编辑的位置。要让光标停在新输入字符后方,需要追踪用户操作前的光标位置,再计算格式化后的对应位置。
修改后的完整代码
// 定义变量追踪光标位置和操作状态 private var lastCursorPosition = 0 private var isAddingText = false private var currentFormattedText = "" binding.editText1.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { // 记录用户操作前的光标位置 lastCursorPosition = binding.editText1.selectionStart // 判断是添加字符还是删除字符 isAddingText = after > count } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun afterTextChanged(s: Editable?) { val inputText = s.toString().trim() // 避免重复触发(当格式化后的文本和当前输入一致时跳过) if (inputText == currentFormattedText) return binding.editText1.removeTextChangedListener(this) // 1. 处理原始输入,去除所有格式符号(逗号、点) val rawNumberStr = inputText.replace(",", "").replace(".", "") val rawNumber = if (rawNumberStr.isNotEmpty()) rawNumberStr.toDouble() else 0.0 // 2. 印尼语环境的货币格式化 val localeId = Locale("in", "ID") val decimalFormat = NumberFormat.getInstance(localeId) as DecimalFormat decimalFormat.applyPattern("#,###.##") val formattedText = decimalFormat.format(rawNumber) // 3. 计算格式化后的光标位置 var newCursorPosition = calculateCursorPosition(rawNumberStr, formattedText, lastCursorPosition, isAddingText) // 4. 设置文本和光标 binding.editText1.setText(formattedText) // 确保光标位置不越界 newCursorPosition = newCursorPosition.coerceIn(0, formattedText.length) binding.editText1.setSelection(newCursorPosition) // 更新当前格式化文本,避免循环触发 currentFormattedText = formattedText binding.editText1.addTextChangedListener(this) } }) // 核心:计算原始光标位置对应到格式化后的位置 private fun calculateCursorPosition( rawNumberStr: String, formattedText: String, originalCursorPos: Int, isAdding: Boolean ): Int { val rawLength = rawNumberStr.length if (rawLength == 0) return formattedText.length var rawIndex = 0 var formattedIndex = 0 var cursorOffset = 0 while (formattedIndex < formattedText.length && rawIndex < rawLength) { val formattedChar = formattedText[formattedIndex] if (formattedChar.isDigit()) { // 匹配原始数字位置与格式化后的位置 if (rawIndex == originalCursorPos - (if (isAdding) 0 else 1)) { cursorOffset = formattedIndex if (isAdding) cursorOffset += 1 // 添加字符后,光标停在新字符后方 break } rawIndex++ } formattedIndex++ } // 处理未匹配到的情况(比如光标在小数位后) return if (cursorOffset == 0) { formattedText.indexOf('.').takeIf { it != -1 }?.plus(3) ?: formattedText.length } else { cursorOffset } }
关键逻辑说明
- 追踪操作状态:
- 在
beforeTextChanged中记录用户操作前的光标位置,以及是添加还是删除字符,这是计算新光标位置的基础。
- 在
- 分离原始输入与格式化结果:
- 先把用户输入的文本去掉所有格式符号(逗号、点),得到纯数字字符串,再进行格式化,避免格式符号干扰光标计算。
- 光标位置映射:
calculateCursorPosition函数遍历格式化后的字符串,将原始数字字符串的位置对应到带格式的字符串中。如果是添加字符,光标会自动移到新输入字符的后方;如果是删除,光标保持在删除位置的前方。
- 避免无限循环:
- 用
currentFormattedText保存上一次格式化后的结果,当输入文本和这个结果一致时跳过处理,防止TextWatcher反复触发。
- 用
额外优化点
- 处理小数部分:如果用户在小数位输入,光标会自动停在小数后的正确位置。
- 边界处理:用
coerceIn确保光标位置不会超出文本长度范围。
内容的提问来源于stack exchange,提问作者itsmebil
相关产品推荐
相关产品推荐

