Android Kotlin使用TextWatcher.onTextChanged实现加油字段联动触发无限循环问题
解决方案
问题根因
- 直接在
TextWatcher的回调中调用setText()会再次触发TextWatcher回调,形成无限循环 - 没有对输入值做合法性校验和异常捕获,空输入、非数字输入、除零操作都会触发
NumberFormatException - 没有适配第三个输入字段
fuelCost的监听,也没有判断当前触发变化的字段,无法正确选择计算逻辑方向
修改后实现代码
// 计算锁标志位,避免setText时触发循环回调 var isCalculating = false val refuelTextWatcher = object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun afterTextChanged(s: Editable?) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { // 正在计算中直接返回,避免循环触发 if (isCalculating) return // 统一封装输入转Float的逻辑,捕获转换异常 fun getFloatFromInput(text: CharSequence?): Float? { return try { text?.toString()?.takeIf { it.isNotEmpty() }?.toFloat() } catch (e: NumberFormatException) { null } } val amount = getFloatFromInput(fuelAmount.text) val pricePerUnit = getFloatFromInput(fuelPricePerUnit.text) val cost = getFloatFromInput(fuelCost.text) isCalculating = true try { // 场景1:油量和单价都有效,计算总花费 if (amount != null && amount > 0 && pricePerUnit != null) { val finalCost = amount * pricePerUnit // 保留2位小数可根据需求调整 fuelCost.setText("%.2f".format(finalCost)) // 光标移动到末尾,优化输入体验 fuelCost.setSelection(fuelCost.text.length) } // 场景2:油量和总花费都有效,计算单价 else if (amount != null && amount > 0 && cost != null) { val finalPrice = cost / amount fuelPricePerUnit.setText("%.2f".format(finalPrice)) fuelPricePerUnit.setSelection(fuelPricePerUnit.text.length) } } finally { // 计算完成重置标志位 isCalculating = false } } } // 三个输入框都要添加监听 fuelAmount.addTextChangedListener(refuelTextWatcher) fuelPricePerUnit.addTextChangedListener(refuelTextWatcher) fuelCost.addTextChangedListener(refuelTextWatcher)
关键优化说明
- 新增
isCalculating锁标志位,计算过程中拦截TextWatcher的回调,彻底解决无限循环问题 - 统一封装输入值转Float的逻辑,捕获转换异常,避免非法输入导致崩溃
- 增加油量大于0的校验,提前拦截除零等非法计算场景
- 两个计算场景互斥执行,不会出现互相覆盖的问题
- 数值格式化可选保留小数位数,优化展示效果,同时设置光标位置提升输入体验
内容的提问来源于stack exchange,提问作者Adrian Bartoszek
相关产品推荐
相关产品推荐

