让你的 RecyclerView 支持多选

下图演示的是 RecyclerView 图片多选,未使用第三方库。如何让 RecyclerView 支持多选,可以从几个方面思考,我们可以按下面的构思来设计这个小功能。

Demo

  • 视图:Item 的长相应该有个可以让用户点击选择、可以显示选择状态的控件,所以 Item 布局可以有一个 CheckBox
  • 数据模型:Item 的数据模型需要能够表达、记录这个对象的 Select 状态,所以 Item 的数据模型应该有一个 boolean is_selected 的属性。
  • 控制器:当用户的多选发生变化时,需要能够获取多选数据,反映出一个多选功能的状况。

1. 布局

下面的代码是 RecyclerView Item 的布局。2 个 View 放在 RelativeLayout 里叠起来便成了开头 Demo 图呈现的那个样子。

  • ImageView (id itemview_imageview),显示图片。
  • CheckBox (id itemview_checkbox),支持用户点击选择,这个是半透的。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="1dp">
    
    <ImageView
        android:id="@+id/itemview_imageview"
        android:contentDescription="@null"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop" />
    
    <CheckBox
        android:id="@+id/itemview_checkbox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#80ffffff"
        android:maxLines="1"
        android:ellipsize="end" />
    </RelativeLayout>
    

2. 数据模型

Item 的数据模型如下代码所示。

  • is_selected 如前面提到的,用来记录这个 Item 的选择状态。
  • bitmap 是图片,例子图片是从 raw 加载的所以用了 Bitmap 类型。
  • title 图片的 title,可以显示在 CheckBox 上。

    class ItemData {
    String title;
    Bitmap bitmap;
    boolean is_selected;
    
    ItemData(String title, Bitmap bitmap, boolean selected) {
        this.title = title;
        this.bitmap = bitmap;
        this.is_selected = selected;
    }
    }
    

3. 适配器

适配器的代码我列出了如下几个部分

  • ItemView 作为 Item 的 ViewHolder,为了避免低效的在 onBindViewHolder 里做 findViewById,这里缓存了 2 个 View 的引用。
  • onBindViewHolder 当一个 Item 显示的时候,根据 ItemData(如上面提到的 bitmap、is_selected、title)来设置这个 Item 的显示,如果之前有选择状态就会被设为 Checked。
  • onCheckedChanged 当一个 Item 显示的时候,也注册了的 onCheckedChanged 事件侦听,当该 Item 的选择状态改变则会存入对应(position)的 ItemData。
  • getSelected 这个方法返回一个 selection 列表,作为演示我在选择改变时打印输出了选择状况。

    static class ItemView extends RecyclerView.ViewHolder {
    ImageView mImageView;
    CheckBox mCheckBox;
    
    ItemView(View itemView) {
        super(itemView);
    
        mImageView = itemView.findViewById(R.id.itemview_imageview);
        mCheckBox = itemView.findViewById(R.id.itemview_checkbox);
    }
    }
    
    @Override
    public void onBindViewHolder(ItemView holder, int position) {
    final ItemData item = mItems.get(position);
    
    holder.mImageView.getLayoutParams().height = ROW_HEIGHT;
    holder.mImageView.setImageBitmap(item.bitmap);
    
    holder.mCheckBox.setText(item.title);
    holder.mCheckBox.setChecked(item.is_selected);
    holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            item.is_selected = b;
    
            StringBuilder sb = new StringBuilder();
            for (ItemData item : getSelected()) {
                sb.append(item.title).append(", ");
            }
    
            android.util.Log.i("selected:", sb.toString());
        }
    });
    }
    
    private ArrayList<ItemData> getSelected() {
    ArrayList<ItemData> selects = new ArrayList<>();
    for (ItemData item : mItems) {
        if (item.is_selected) {
            selects.add(item);
        }
    }
    
    return selects;
    }
    

4. 运行

本文的思路是给每个 Item 对象添加选择数据,每当有选择改变时查找所有被选择的 Item 并打印出来。当有选择变化时输出信息如下,即实现了多选的功能。

logcat


2017-08-18