Android RecyclerView长按单个项后如何显示所有项的复选框?
解决RecyclerView长按显示所有复选框的问题&Adapter工作机制解析
问题根源分析
你现在的代码里,长按事件只修改了当前被长按的ViewHolder的复选框可见性,但RecyclerView的ViewHolder是复用机制,而且没有一个全局状态来统一控制所有item的显示状态。其他未被绑定的item(或者已经复用的ViewHolder)不会自动更新,所以只有被长按的那一项会显示复选框,其他项没变化。
解决方案步骤
我们需要给Adapter添加一个全局状态变量,用来标记是否进入“选择模式”,然后通过通知Adapter更新所有item,让每个item根据这个状态来显示/隐藏复选框。同时为了后续的多选删除,还需要记录选中的item。
1. 添加全局状态变量和选中项记录
在Adapter类里添加这两个变量:
// 标记是否处于选择模式 private boolean isSelectionMode = false; // 记录选中的item位置(SparseBooleanArray比HashMap更适合存位置和选中状态) private SparseBooleanArray selectedItems = new SparseBooleanArray();
2. 修改长按事件逻辑
长按的时候切换选择模式状态,然后通知所有item重新绑定:
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { // 切换选择模式 isSelectionMode = true; // 默认选中当前长按的item selectedItems.put(position, true); // 通知所有item重新绑定数据,更新复选框状态 notifyDataSetChanged(); return true; } });
3. 在onBindViewHolder中统一控制复选框显示
每次绑定item的时候,根据选择模式状态和选中状态来设置复选框:
@Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.coffret_title.setText(mDataFiltered.get(position).getTitle()); Picasso.with(mContext).load(mDataFiltered.get(position).getUri()) .placeholder(R.drawable.placeholder) .resize(500,500) .centerCrop() .into(holder.coffret_thumbnail); // 根据选择模式设置复选框可见性 holder.checkBox.setVisibility(isSelectionMode ? View.VISIBLE : View.GONE); // 设置复选框的选中状态 holder.checkBox.setChecked(selectedItems.get(position, false)); // 点击事件:选择模式下切换选中状态,否则跳转页面 holder.cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isSelectionMode) { // 切换当前item的选中状态 boolean isChecked = !selectedItems.get(position, false); selectedItems.put(position, isChecked); holder.checkBox.setChecked(isChecked); } else { Toast.makeText(mContext, "" + mDataFiltered.get(position).getTitle(), Toast.LENGTH_SHORT).show(); Intent intent = new Intent(mContext, SelectedPicPreviewActivity.class); intent.putExtra("image_url", mDataFiltered.get(position).getUri().toString()); mContext.startActivity(intent); } } }); holder.cardView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { isSelectionMode = true; selectedItems.put(position, true); notifyDataSetChanged(); return true; } }); // 复选框点击事件(可选,和item点击联动) holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean isChecked = ((CircleCheckBox)v).isChecked(); selectedItems.put(position, isChecked); } }); }
4. 添加退出选择模式和删除选中项的方法
方便后续调用(比如在Activity的返回键点击时退出,或者删除按钮点击时删除):
// 退出选择模式 public void exitSelectionMode() { isSelectionMode = false; selectedItems.clear(); notifyDataSetChanged(); } // 获取选中的item列表 public ArrayList<Picture> getSelectedItems() { ArrayList<Picture> selectedList = new ArrayList<>(); for (int i = 0; i < mDataFiltered.size(); i++) { if (selectedItems.get(i, false)) { selectedList.add(mDataFiltered.get(i)); } } return selectedList; } // 删除选中的item public void deleteSelectedItems() { // 注意要从后往前删,避免索引混乱 for (int i = mDataFiltered.size() - 1; i >= 0; i--) { if (selectedItems.get(i, false)) { mDataFiltered.remove(i); mData.remove(mDataFiltered.get(i)); // 如果需要同步原始数据 } } exitSelectionMode(); notifyDataSetChanged(); }
RecyclerView Adapter工作机制解析
要理解为什么之前的代码不行,得先搞懂Adapter这几个核心方法的作用:
onCreateViewHolder: 当RecyclerView需要一个新的ViewHolder来显示item时调用,它会创建ViewHolder并绑定item布局。RecyclerView会把创建好的ViewHolder放进复用池,当item滑出屏幕时,ViewHolder会被回收,后续新的item进入屏幕时会优先从复用池里取,而不是重新创建。onBindViewHolder: 当item需要显示数据(第一次显示或者从复用池里取出复用)时调用,负责把数据绑定到ViewHolder的控件上。这也是为什么我们要在这里设置复选框的可见性——因为每次item显示都会走这个方法,能保证状态正确。notifyDataSetChanged(): 通知Adapter数据或者状态发生了变化,RecyclerView会重新调用所有可见item的onBindViewHolder方法,更新显示内容。如果只需要更新单个item,可以用notifyItemChanged(int position),效率更高。
这样修改后,长按任意一项就会触发所有item显示复选框,并且可以选中多个item进行删除操作啦。
内容的提问来源于stack exchange,提问作者Amine
相关产品推荐
相关产品推荐

