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。
修改步骤
- 延迟UI更新:将
notifyDataSetChanged()放到view.post()中,让更新操作在当前布局计算完成后执行; - 避免重复设置监听器:把复选框的监听器移到
ViewHolder构造方法中,防止onBindViewHolder重复调用导致的逻辑重复触发; - 优化全选判断逻辑:取消非全选项时,判断是否还有其他选中项,再同步更新全选框状态。
修改后的关键代码
重构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
相关产品推荐
相关产品推荐

