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

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
    }
}

关键逻辑说明

  1. 追踪操作状态:
    • 在beforeTextChanged中记录用户操作前的光标位置,以及是添加还是删除字符,这是计算新光标位置的基础。
  2. 分离原始输入与格式化结果:
    • 先把用户输入的文本去掉所有格式符号(逗号、点),得到纯数字字符串,再进行格式化,避免格式符号干扰光标计算。
  3. 光标位置映射:
    • calculateCursorPosition函数遍历格式化后的字符串,将原始数字字符串的位置对应到带格式的字符串中。如果是添加字符,光标会自动移到新输入字符的后方;如果是删除,光标保持在删除位置的前方。
  4. 避免无限循环:
    • 用currentFormattedText保存上一次格式化后的结果,当输入文本和这个结果一致时跳过处理,防止TextWatcher反复触发。

额外优化点

  • 处理小数部分:如果用户在小数位输入,光标会自动停在小数后的正确位置。
  • 边界处理:用coerceIn确保光标位置不会超出文本长度范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 23:24:31