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

Android RecyclerView多选功能触发崩溃问题求助

RecyclerView多选功能崩溃问题

问题现象

  • 选中列表第0项(全选项)时,所有列表项可正常选中/取消选中;
  • 当所有项处于选中状态时,取消第1、2或3项的复选框,期望同步取消第0项的复选框,此时应用直接崩溃。

错误日志

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling androidx.recyclerview.widget.RecyclerView{6631a12 VFED..... ......ID 0,0-1048,1400 #7f090731 app:id/vagozariSelectedAmlak}, adapter:com.adapters.VagozariSelectedAmlakAdapter@60fc5e3, layout:androidx.recyclerview.widget.LinearLayoutManager@3949be0, context:com.view.activity.vagozariMelk.makeVagozary.SelectedAmlakByUserActivity@3b4a0d1
        at androidx.recyclerview.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:3051)
        at androidx.recyclerview.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:5536)
        at androidx.recyclerview.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:12253)
        at androidx.recyclerview.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:7354)
        at com.VagozariSelectedAmlakAdapter$1.onCheckedChanged(VagozariSelectedAmlakAdapter.java:114)
        at android.widget.CompoundButton.setChecked(CompoundButton.java:180)
        at com.app.adapters.VagozariSelectedAmlakAdapter.onBindViewHolder(VagozariSelectedAmlakAdapter.java:69)
        at com.app.adapters.VagozariSelectedAmlakAdapter.onBindViewHolder(VagozariSelectedAmlakAdapter.java:26)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
        at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1873)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1873)
        at android.view.View.layout(View.java:21912)
        at android.view.ViewGroup.layout(ViewGroup.java:6260)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)

相关代码

Adapter类代码

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;

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


    private Context context;
    private ArrayList<ClientsDataSet> listAmlak;
    boolean isCheckAll = false;
    ItemClickListener itemClickListener;
    int selectedPosition = -1;

    public static ArrayList<String> clientsList = new ArrayList<>();
    private RadioButton lastCheckedRB = null;



    public VagozariSelectedAmlakAdapter(Context context, ArrayList<ClientsDataSet> listAmlak, ItemClickListener itemClickListener) {
        this.context = (Activity) context;
        this.listAmlak = listAmlak;
        this.itemClickListener = itemClickListener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_archive_selcted_amlak, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull @NotNull VagozariSelectedAmlakAdapter.ViewHolder holder, int position) {
        if (listAmlak.get(position).selected == true) {
            holder.radioButton.setChecked(true);
        } else {
            holder.radioButton.setChecked(false);
        }

        if (listAmlak.get(position).name != null) {
            holder.name_amlak.setText(listAmlak.get(position).name);
        } else {
            holder.name_amlak.setVisibility(View.GONE);
            holder.radioButton.setVisibility(View.GONE);
        }

        holder.radioButton.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton compoundButton, boolean statet) {
                        if (statet) {
                            if (position == 0) {
                                for (int i = 0; i < listAmlak.size(); i++) {
                                    ClientsDataSet dataSet = listAmlak.get(i);
                                    dataSet.selected = true;
                                    listAmlak.set(i, dataSet);
                                }
                                notifyDataSetChanged();
                            } else {
                                ClientsDataSet dataSet = listAmlak.get(position);
                                dataSet.selected = true;
                                listAmlak.set(position, dataSet);

                                ClientsDataSet alltfalse = listAmlak.get(0);
                                alltfalse.selected = false;
                                listAmlak.set(0, alltfalse);
                                notifyDataSetChanged();
                            }
                        } else {
                            if (position == 0) {
                                for (int i = 0; i < listAmlak.size(); i++) {
                                    ClientsDataSet dataSet = listAmlak.get(i);
                                    dataSet.selected = false;
                                    listAmlak.set(i, dataSet);
                                    isCheckAll = false;
                                }
                                notifyDataSetChanged();
                            } else {
                                ClientsDataSet dataSet = listAmlak.get(position);
                                dataSet.selected = false;
                                listAmlak.set(position, dataSet);

                                ClientsDataSet alltfalse = listAmlak.get(0);
                                alltfalse.selected = false;
                                listAmlak.set(0, alltfalse);
                                notifyDataSetChanged();
                            }
                        }
                    }
                });
    }

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

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        CheckBox radioButton;
        TextView name_amlak;
        ConstraintLayout root;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            radioButton = itemView.findViewById(R.id.item_vagozary_radioButton_selceting_amlak);
            name_amlak = itemView.findViewById(R.id.item_vagozary_name_amlak);
            root = itemView.findViewById(R.id.root);
        }
    }

    public interface ItemClickListener {
        void onClick(String s);
    }
}

主页面调用Adapter代码

LinearLayoutManager layoutManager = new LinearLayoutManager(SelectedAmlakActivityV2, LinearLayoutManager.VERTICAL, false);
lists.setLayoutManager(layoutManager);
ClientsDataSet item = new ClientsDataSet();
item.name = "all";
apiClients.clients.add(0, item);

adapter = new VagozariSelectedAmlakAdapter(SelectedAmlakActivityV2, apiClients.clients, itemClickListener);
lists.setAdapter(adapter);

解决方案

崩溃核心原因:在RecyclerView正在计算布局或滚动时调用了notifyDataSetChanged(),违反了RecyclerView的操作规则,直接触发IllegalStateException。

修改步骤

  1. 延迟UI更新:将notifyDataSetChanged()放到view.post()中,让更新操作在当前布局计算完成后执行;
  2. 避免重复设置监听器:把复选框的监听器移到ViewHolder构造方法中,防止onBindViewHolder重复调用导致的逻辑重复触发;
  3. 优化全选判断逻辑:取消非全选项时,判断是否还有其他选中项,再同步更新全选框状态。

修改后的关键代码

重构ViewHolder类

public class ViewHolder extends RecyclerView.ViewHolder {
    CheckBox radioButton;
    TextView name_amlak;
    ConstraintLayout root;

    public ViewHolder(@NonNull View itemView) {
        super(itemView);
        radioButton = itemView.findViewById(R.id.item_vagozary_radioButton_selceting_amlak);
        name_amlak = itemView.findViewById(R.id.item_vagozary_name_amlak);
        root = itemView.findViewById(R.id.root);

        // 监听器移到ViewHolder构造,避免重复绑定
        radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean statet) {
                int position = getAdapterPosition();
                if (position == RecyclerView.NO_POSITION) return; // 无效位置直接返回

                if (statet) {
                    if (position == 0) {
                        // 全选逻辑
                        for (int i = 0; i < listAmlak.size(); i++) {
                            ClientsDataSet dataSet = listAmlak.get(i);
                            dataSet.selected = true;
                            listAmlak.set(i, dataSet);
                        }
                    } else {
                        // 选中单个项,取消全选框
                        ClientsDataSet dataSet = listAmlak.get(position);
                        dataSet.selected = true;
                        listAmlak.set(position, dataSet);

                        ClientsDataSet allItem = listAmlak.get(0);
                        allItem.selected = false;
                        listAmlak.set(0, allItem);
                    }
                } else {
                    if (position == 0) {
                        // 取消全选逻辑
                        for (int i = 0; i < listAmlak.size(); i++) {
                            ClientsDataSet dataSet = listAmlak.get(i);
                            dataSet.selected = false;
                            listAmlak.set(i, dataSet);
                            isCheckAll = false;
                        }
                    } else {
                        // 取消单个项,判断是否还有其他选中项
                        ClientsDataSet dataSet = listAmlak.get(position);
                        dataSet.selected = false;
                        listAmlak.set(position, dataSet);

                        boolean hasAnySelected = false;
                        for (int i = 1; i < listAmlak.size(); i++) {
                            if (listAmlak.get(i).selected) {
                                hasAnySelected = true;
                                break;
                            }
                        }
                        ClientsDataSet allItem = listAmlak.get(0);
                        allItem.selected = hasAnySelected;
                        listAmlak.set(0, allItem);
                    }
                }

                // 延迟执行更新,避开布局计算阶段
                itemView.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyDataSetChanged();
                    }
                });
            }
        });
    }
}

简化onBindViewHolder方法

移除原有的监听器设置,只保留数据绑定逻辑:

@Override
public void onBindViewHolder(@NonNull @NotNull VagozariSelectedAmlakAdapter.ViewHolder holder, int position) {
    // 先清空监听器,避免绑定过程中误触发
    holder.radioButton.setOnCheckedChangeListener(null);

    // 绑定选中状态
    holder.radioButton.setChecked(listAmlak.get(position).selected);

    // 绑定名称
    if (listAmlak.get(position).name != null) {
        holder.name_amlak.setText(listAmlak.get(position).name);
        holder.name_amlak.setVisibility(View.VISIBLE);
        holder.radioButton.setVisibility(View.VISIBLE);
    } else {
        holder.name_amlak.setVisibility(View.GONE);
        holder.radioButton.setVisibility(View.GONE);
    }
}

内容的提问来源于stack exchange,提问作者Amin Azizzadeh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 20:50:28