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

Android RecyclerView删除后notify更新列表偶发失效问题求助

问题:RecyclerView删除功能时好时坏,notify更新列表失效

删除功能时好时坏,耗时3天排查bug仍未定位根因,初步怀疑问题出在notifyItemRemoved方法调用或是数组元素移除逻辑上,尝试过绝大多数用于更新RecyclerView的notify系列方法,均无法让列表正常更新。

对应适配器类代码

public class ChecklistItems extends AppCompatActivity {
Toolbar toolbar;
RecyclerView recyclerView;

String content;
String status;

String[] contentSplit;
String[] statusSplit;

private ChecklistAdapter checklistAdapter;
DbHelper dbHelper;
List<ChecklistHelper> checks;
int id;
ChecklistHelper checklistHelper;
StringBuilder stringBuilderContent;
StringBuilder stringBuilderStatus;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_checklist_items);

    toolbar = findViewById(R.id.tool_bar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    recyclerView = findViewById(R.id.recycler_view_child);


    id = getIntent().getIntExtra("id", 0);
    content = getIntent().getStringExtra("content");
    status = getIntent().getStringExtra("status");

    contentSplit = content.split("\n");
    statusSplit = status.split("\n");

    checklistHelper = new ChecklistHelper();
    dbHelper = new DbHelper(this);

    checks = dbHelper.getChecklists();
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    
    checklistHelper =  dbHelper.getChecklists(id);
    String status = checklistHelper.getStatus();
    String[] statusSplit = status.split("\n");
    ArrayList<String> newStatusList = new ArrayList<>(Arrays.asList(statusSplit));
    ArrayList<String> newContentList = new ArrayList<>(Arrays.asList(statusSplit));

    checklistAdapter = new ChecklistAdapter(newStatusList, newContentList);
    recyclerView.setAdapter(checklistAdapter);
}


public class ChecklistAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    ArrayList<String> newStatusList;
    ArrayList<String> newContentList;
    
    public ChecklistAdapter( ArrayList<String> newStatusList, ArrayList<String> newContentList) {
        
        this.newStatusList = newStatusList;
        this.newContentList = newContentList;
    }
    
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.checklist_layout_child, parent, false);
        return new ChecklistChildViewHolder(view);

    }
    
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint("RecyclerView") int position) {

        ChecklistChildViewHolder checklistChildViewHolder = (ChecklistChildViewHolder) holder;
        checklistChildViewHolder.checkBox.setText(contentSplit[position]);

        boolean state;
        if (statusSplit[position].equals("1")) {
            state = true;
            checklistChildViewHolder.checkBox.setText(lineThrough(checklistChildViewHolder.checkBox.getText().toString()));
        }
        else
        {
            state = false;
            checklistChildViewHolder.checkBox.setText(checklistChildViewHolder.checkBox.getText().toString());
        }
        checklistChildViewHolder.checkBox.setChecked(state);

        checklistChildViewHolder.delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                doDelete(position);
            }
        });
    }

    @Override
    public int getItemCount() {
        return newStatusList.size();

    }

    public class ChecklistChildViewHolder extends RecyclerView.ViewHolder {
        CheckBox checkBox;
        ImageView delete, edit;

        ChecklistChildViewHolder(@NonNull View itemView) {
            super(itemView);
            checkBox = itemView.findViewById(R.id.checkbox_child);
            delete = itemView.findViewById(R.id.delete_child);
            edit = itemView.findViewById(R.id.edit_child);
            delete.setVisibility(View.INVISIBLE);
            edit.setVisibility(View.INVISIBLE);
        }

        public void updateVisibility(boolean isEnableDelete){

            if (isEnableDelete) {
                delete.setVisibility(View.VISIBLE);
                edit.setVisibility(View.VISIBLE);
            }
            else {
                delete.setVisibility(View.INVISIBLE);
                edit.setVisibility(View.INVISIBLE);
            }
        }
    }

    public int doDelete(int position) {

        int idd = getIntent().getIntExtra("id", 0);
        checklistHelper =  dbHelper.getChecklists(idd);

        String status = checklistHelper.getStatus();
        String content = checklistHelper.getContent();

        String[] statusSplit = status.split("\n");
        String[] contentSplit = content.split("\n");

        newStatusList = new ArrayList<>(Arrays.asList(statusSplit));
        newContentList = new ArrayList<>(Arrays.asList(contentSplit));

        newStatusList.remove(position);
        newContentList.remove(position);
        
        stringBuilderStatus = new StringBuilder();
        stringBuilderContent = new StringBuilder();

        for(String singleStatus : newStatusList) {
            stringBuilderStatus.append(singleStatus);
            stringBuilderStatus.append("\n");
        }

        for(String singleContent: newContentList){
            stringBuilderContent.append(singleContent);
            stringBuilderContent.append("\n");
        }
        String allStatus = stringBuilderStatus.toString();
        String allContent = stringBuilderContent.toString();
        dbHelper.updateChecklist(id,allStatus , allContent, DateTime.date(), DateTime.time(), System.currentTimeMillis());

        if(allContent.length()==0) {
            dbHelper.deleteChecklist(id);
            finish();
        }
        
        notifyItemRemoved(position);
        notifyItemChanged(position);

        return newStatusList.size();
    }
}
}

onCreate生命周期方法实现代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_checklist_items);

    toolbar = findViewById(R.id.tool_bar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    recyclerView = findViewById(R.id.recycler_view_child);
    
    id = getIntent().getIntExtra("id", 0);
    content = getIntent().getStringExtra("content");
    status = getIntent().getStringExtra("status");

    contentSplit = content.split("\n");
    statusSplit = status.split("\n");

    checklistHelper = new ChecklistHelper();
    dbHelper = new DbHelper(this);
    
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    recyclerView.setAdapter(checklistAdapter);


    checklistHelper =  dbHelper.getChecklists(id);
    String status = checklistHelper.getStatus();
    String[] statusSplit = status.split("\n");
    ArrayList<String> newStatusList = new ArrayList<>(Arrays.asList(statusSplit));
    ArrayList<String> newContentList = new ArrayList<>(Arrays.asList(statusSplit));

    checklistAdapter = new ChecklistAdapter(newStatusList, newContentList);
    recyclerView.setAdapter(checklistAdapter);
}

问题根因&修复方案

代码里存在4个直接导致删除功能异常的问题:

  • 数据源引用错乱
    onBindViewHolder里加载列表内容用的是Activity级别的全局数组contentSplit、statusSplit,这两个数组只在onCreate初始化时从Intent取了一次值,删除操作后从来没有更新过。删除后列表重绑item时还是读的旧数组数据,自然显示不对。另外初始化newContentList的时候错误传了statusSplit,内容列表和状态列表用了同一份数据,从初始化阶段内容显示就可能出错。
  • 删除逻辑重复创建新列表,替换了适配器持有的原列表引用
    doDelete方法里每次都重新从数据库读数据、new新的ArrayList赋值给newStatusList和newContentList,相当于把适配器原来持有的列表引用直接替换了,但notify系列方法是基于适配器持有的原数据集计算位置差异的,直接换引用会导致适配器内部的位置缓存和实际数据集不匹配,出现位置错乱、删错项、列表不刷新的问题。
  • 点击事件持有过期position
    onBindViewHolder里给删除按钮设点击监听的时候直接用了方法参数里的position,当列表有item被删除后,没有重新绑定的holder持有的还是旧的position值,触发点击时会操作错误位置。
  • notify调用逻辑错误、onCreate存在重复初始化
    调用notifyItemRemoved(position)之后不需要再调用notifyItemChanged(position),正确做法是删除后调用notifyItemRangeChanged(position, newStatusList.size() - position)刷新被删位置之后所有item的位置,避免位置缓存错乱。另外onCreate里先给recyclerView设了一次未初始化的空adapter,之后又重新new adapter再set一次,属于无意义的重复操作,也可能引发状态异常。

核心修复代码参考

首先修正onCreate里的初始化逻辑,去掉重复setAdapter的代码,初始化newContentList时传入contentSplit而非statusSplit;再修改绑定和删除逻辑:

// 修正后的onBindViewHolder内容加载与点击绑定逻辑
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    ChecklistChildViewHolder checklistChildViewHolder = (ChecklistChildViewHolder) holder;
    // 直接读取适配器持有的列表数据,不要用Activity全局的旧数组
    checklistChildViewHolder.checkBox.setText(newContentList.get(position));
    boolean state = "1".equals(newStatusList.get(position));
    if (state) {
        checklistChildViewHolder.checkBox.setText(lineThrough(checklistChildViewHolder.checkBox.getText().toString()));
    } else {
        checklistChildViewHolder.checkBox.setText(checklistChildViewHolder.checkBox.getText().toString());
    }
    checklistChildViewHolder.checkBox.setChecked(state);

    checklistChildViewHolder.delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // 获取实时位置,不要用绑定时传入的固定position
            int currentPos = checklistChildViewHolder.getAdapterPosition();
            if(currentPos != RecyclerView.NO_POSITION){
                doDelete(currentPos);
            }
        }
    });
}

// 修正后的doDelete核心逻辑
public int doDelete(int position) {
    // 直接操作适配器当前持有的列表,不要重新new列表替换引用
    newStatusList.remove(position);
    newContentList.remove(position);
    
    // 拼接更新数据库的逻辑保持不变
    stringBuilderStatus = new StringBuilder();
    stringBuilderContent = new StringBuilder();
    for(String singleStatus : newStatusList) {
        stringBuilderStatus.append(singleStatus).append("\n");
    }
    for(String singleContent: newContentList){
        stringBuilderContent.append(singleContent).append("\n");
    }
    String allStatus = stringBuilderStatus.toString();
    String allContent = stringBuilderContent.toString();
    dbHelper.updateChecklist(id, allStatus, allContent, DateTime.date(), DateTime.time(), System.currentTimeMillis());

    if(allContent.length()==0) {
        dbHelper.deleteChecklist(id);
        finish();
        return 0;
    }
    // 正确调用notify方法
    notifyItemRemoved(position);
    // 刷新被删位置之后的所有item,修正位置缓存
    notifyItemRangeChanged(position, newStatusList.size() - position);

    return newStatusList.size();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:27:49