ExpandableListView子项编辑异常:修改子项触发其他子项同步变更
问题根源与解决方案
你的问题本质是ExpandableListView的View复用机制导致的数据串位——子项View会被重复利用来展示不同的Child数据,如果没有正确处理复用逻辑,旧的事件监听或数据绑定会干扰新的子项,引发无关子项同步变更的问题。以下是针对性的解决步骤和代码修正方案:
1. 先确保Child对象的独立性
首先检查你的数据初始化逻辑,必须保证每个子项对应唯一的Child实例,禁止多个子项共享同一个Child对象(这会导致修改一个子项时,其他引用该对象的子项同步变化)。
错误示例(禁止):
Child sharedChild = new Child(true, ""); parent1.addChild(sharedChild); parent2.addChild(sharedChild); // 多个子项共用同一个对象
正确示例:
// 每个子项都创建独立的Child实例 Parent parent1 = new Parent("Parent1"); parent1.addChild(new Child(true, "")); // Child1a parent1.addChild(new Child(true, "")); // Child1b Parent parent2 = new Parent("Parent2"); parent2.addChild(new Child(true, "")); // Child2a parent2.addChild(new Child(true, "")); // Child2b parent2.addChild(new Child(true, "")); // Child2c
2. 优化Adapter的getChildView方法(核心修复)
修改MyExpandableListAdapter的getChildView方法,完善ViewHolder复用逻辑,清除旧监听并严格绑定当前Child数据:
@Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ViewHolder holder; Child currentChild = (Child) getChild(groupPosition, childPosition); if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.child_item_layout, parent, false); holder = new ViewHolder(); holder.radioGroup = convertView.findViewById(R.id.radio_group); holder.rbYes = convertView.findViewById(R.id.rb_yes); holder.rbNo = convertView.findViewById(R.id.rb_no); holder.etReason = convertView.findViewById(R.id.et_reason); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); // 复用前清除旧监听,避免串位 if (holder.textWatcher != null) { holder.etReason.removeTextChangedListener(holder.textWatcher); } holder.radioGroup.setOnCheckedChangeListener(null); } // 严格根据当前Child的状态设置View属性 holder.rbYes.setChecked(currentChild.isYes()); holder.rbNo.setChecked(!currentChild.isYes()); holder.etReason.setVisibility(currentChild.isYes() ? View.GONE : View.VISIBLE); holder.etReason.setText(currentChild.getReason()); // 绑定RadioGroup点击事件,仅更新当前Child holder.radioGroup.setOnCheckedChangeListener((group, checkedId) -> { boolean isYes = checkedId == holder.rbYes.getId(); currentChild.setYes(isYes); holder.etReason.setVisibility(isYes ? View.GONE : View.VISIBLE); // 刷新列表确保状态同步 notifyDataSetChanged(); }); // 绑定EditText输入监听,实时更新当前Child的备注 holder.textWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { currentChild.setReason(s.toString()); } }; holder.etReason.addTextChangedListener(holder.textWatcher); return convertView; } // 完善ViewHolder类,保存TextWatcher用于复用前移除 static class ViewHolder { RadioGroup radioGroup; RadioButton rbYes; RadioButton rbNo; EditText etReason; TextWatcher textWatcher; }
3. 保存数据的逻辑
点击保存按钮时,直接遍历你的parentList集合,逐个获取每个Parent下的Child对象的状态和备注即可:
// 保存数据示例 List<Parent> parentList = mAdapter.getParentList(); for (Parent parent : parentList) { for (Child child : parent.getChildList()) { boolean isYes = child.isYes(); String reason = child.getReason(); // 执行保存逻辑(如存入数据库、SP或上传服务器) } }
关键注意点
- 复用View时必须清除旧的
TextWatcher和RadioGroup监听器,否则旧监听会绑定到新的Child对象,导致数据串位。 - 所有View的状态(RadioButton选中、EditText显示/隐藏、文本内容)必须严格根据当前Child对象的属性设置,不能依赖View的缓存状态。
- 若想避免全局刷新列表,可使用
notifyChildChanged(groupPosition, childPosition)替代notifyDataSetChanged()(需注意API兼容性)。
内容的提问来源于stack exchange,提问作者Fletch
相关产品推荐
相关产品推荐

