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

Android RecyclerView滚动时子项CheckBox、EditText值自动清空问题

RecyclerView滚动时子项CheckBox/EditText状态丢失解决方案

问题根源

RecyclerView自带View复用机制:滚动时离开可视区域的ItemView会被回收,复用到新进入屏幕的子项上。当前实现存在两个核心问题:

  • 绑定子项时强制将CheckBox重置为未选中状态,没有读取历史存储的状态
  • 没有将用户操作产生的勾选状态、输入内容持久化到绑定的数据源中,复用时自然无法还原状态

实现步骤

1. 修改数据实体类,增加状态存储字段

所有控件状态必须存储在和子项绑定的数据源对象中,不能依赖View本身存状态。先修改Child实体类,新增两个状态字段:

public class Child {
    // --- 原有业务字段保留 ---
    private String answerName;
    private int answerId;

    // --- 新增状态存储字段 ---
    private boolean isChecked = false; // 存储CheckBox勾选状态,默认未选中
    private String percentageInput = ""; // 存储EditText输入内容,默认为空

    // 原有getter/setter省略,补充新增字段的访问方法
    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public String getPercentageInput() {
        return percentageInput;
    }

    public void setPercentageInput(String percentageInput) {
        this.percentageInput = percentageInput;
    }
}

如果分组头部的edHeaderPercentage输入框也需要保留状态,给SectionHeader类也加对应的字符串字段存储输入值即可。

2. 修改Adapter子项绑定逻辑

核心原则:所有控件状态从数据源读取绑定,用户操作时立刻将状态回写到对应数据源对象。注意绑定前必须先移除旧的监听器,避免View复用时监听器错乱。
修改onBindChildViewHolder方法实现如下:

@Override
public void onBindChildViewHolder(ChildViewHolder childViewHolder, int sectionPos, int childPos, Child child) {
    // 第一步:先清空控件原有监听器,避免绑定阶段触发错误回调
    childViewHolder.chk_answer.setOnCheckedChangeListener(null);
    // 移除EditText绑定的旧TextWatcher
    if (childViewHolder.etPercentage.getTag() instanceof TextWatcher) {
        childViewHolder.etPercentage.removeTextChangedListener((TextWatcher) childViewHolder.etPercentage.getTag());
    }

    // 第二步:从数据源读取值绑定控件,禁止硬编码重置状态
    childViewHolder.chk_answer.setText(child.getAnswerName());
    childViewHolder.tv_surveyAnswerId.setText(String.valueOf(child.getAnswerId()));
    // 读取存储的勾选状态,替换原有硬编码的setChecked(false)
    childViewHolder.chk_answer.setChecked(child.isChecked());
    // 读取存储的输入内容
    childViewHolder.etPercentage.setText(child.getPercentageInput());
    childViewHolder.etPercentage.setImeOptions(EditorInfo.IME_ACTION_DONE);

    // 第三步:设置监听器,用户操作时实时同步状态到数据源
    // CheckBox选中状态监听
    childViewHolder.chk_answer.setOnCheckedChangeListener((btn, isChecked) -> {
        child.setChecked(isChecked);
        // 原有业务回调保留
        if (listener != null) {
            listener.onItemClick(child, isChecked);
        }
    });

    // EditText输入监听
    TextWatcher inputWatcher = 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) {
            String inputContent = s.toString().trim();
            child.setPercentageInput(inputContent);
            // 原有业务回调保留
            if (listener != null) {
                listener.onTextPercentageChange(String.valueOf(child.getAnswerId()), inputContent);
            }
        }
    };
    // 将Watcher存在Tag中,下次复用时方便移除
    childViewHolder.etPercentage.setTag(inputWatcher);
    childViewHolder.etPercentage.addTextChangedListener(inputWatcher);
}

3. 分组头部输入框状态保留(可选)

如果需要保留分组头部edHeaderPercentage的输入内容,用完全相同的逻辑处理onBindSectionViewHolder即可:

  • 给SectionHeader类加字段存储输入值
  • 绑定前先移除旧的TextWatcher
  • 从数据源读取值设置给EditText
  • 加TextWatcher实时回写输入内容到SectionHeader对象

注意事项

  • 禁止在onBindXXXViewHolder方法中硬编码重置控件状态(比如原有代码的chk_answer.setChecked(false)),所有控件展示值必须以数据源中存储的内容为准
  • 给可输入、可选中的控件设置监听器前,必须先移除旧的监听器,否则View复用时会触发多个监听器回调,导致内容串位、数据错乱
  • 如果后续做下拉刷新、列表数据更新操作,需要做新旧数据的状态映射,不要直接全量替换数据源导致已存储的状态被清空

实现效果参考:
实现效果截图

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:12:29