Android动态inflate两次EditText布局时TextWatcher仅第二个生效
问题根因
这个bug是动态加载布局的经典坑,绝大多数场景都是同一个问题:控件引用被第二次inflate的实例覆盖了,具体触发逻辑:
- 你大概率把长度、宽度、面积输入框定义成了Activity/Fragment的全局变量,每次inflate子布局后,都用全局
findViewById()给这几个变量赋值。第一次inflate完你给这几个变量绑了TextWatcher,等第二次inflate执行,布局树里出现了两个同id的EditText,findViewById()默认返回当前布局层级里最后加载的对应id控件实例,直接把你之前存的第一个布局的控件引用覆盖掉。 - 最终结果就是:第一个布局里的EditText根本没绑定上你写的计算逻辑,就算绑了,计算完赋值的也是你全局变量指向的第二个布局的面积输入框,看起来就像只有第二个布局生效。
剩下极个别情况是绑监听时加了位置判断,第一次inflate时跳过了监听绑定,但几乎不会遇到。
修复方案
核心原则就一条:每个inflate出来的子布局,自己的控件自己找,自己的监听自己绑,别用全局变量存子布局的控件引用。
正确实现代码(Kotlin版本,Java逻辑完全一致):
// 外层承载子布局的根容器 val rootLayout = findViewById<LinearLayout>(R.id.ll_root) // 循环inflate2次 for (i in 0 until 2) { // 每次inflate生成独立的子布局对象 val itemView = layoutInflater.inflate(R.layout.item_measure_layout, rootLayout, false) // 关键:所有子布局内的控件,都通过当前inflate出的itemView查找,不要调用全局findViewById val etLength = itemView.findViewById<EditText>(R.id.et_length) val etWidth = itemView.findViewById<EditText>(R.id.et_width) val etArea = itemView.findViewById<EditText>(R.id.et_area) // 给当前子布局的输入框绑定监听,计算逻辑只操作当前子布局的面积框 val calculateWatcher = object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun afterTextChanged(s: Editable?) { val lengthVal = etLength.text.toString().trim().toDoubleOrNull() ?: 0.0 val widthVal = etWidth.text.toString().trim().toDoubleOrNull() ?: 0.0 val calculateArea = lengthVal * widthVal etArea.setText(if (calculateArea > 0) calculateArea.toString() else "") } } etLength.addTextChangedListener(calculateWatcher) etWidth.addTextChangedListener(calculateWatcher) // 把子布局添加到根容器 rootLayout.addView(itemView) }
注意事项
- 不要把
etLength/etWidth/etArea声明成Activity的全局成员变量,每次inflate时在循环体内声明即可,局部变量不会被后续inflate流程覆盖 - 如果需要后续获取每个子布局输入的值,遍历
rootLayout的子View,再逐个从子View里找对应控件取值即可,不要靠全局变量存 - Java写法只需要替换对应语法即可,核心逻辑不变:子控件从当前inflate的子View上查找,监听绑定在当前子控件上
内容的提问来源于stack exchange,提问作者ashish
相关产品推荐
相关产品推荐

