题记
在查看RecyclerView的官方文档的时候发现了这个recyclerview-selection库,经过测试感觉功能挺好的,省去了自己需要编写大量多选功能的代码,官方文档的guide又不是太清晰,这篇文章仅仅作为简单的记录。
参考:
- 如何将多个选择添加到Android RecyclerView(Kotlin)
- RecyclerView-Selection(Kotlin)
- github示例代码(Java)
- Create a List with RecyclerView——Google
- androidx.recyclerview.selection——Google
说明
根据官方文档的描述,这个库就是用来处理RecyclerView的Item的选择问题,并且可以在设备配置改变的时候保存已选择的数据,重新创建页面的时候再次加载,省去了我们自己对这部分的操作;同时通过观察者模式提供了item点击监听、长按订阅功能。

使用
- 选择一个key的类型。用来构建ItemKeyProvider;可选择的类型目前只有三种: String:基于字符串的稳定标识符可以使用String; Long:当RecyclerView的long stable Id已经在使用时,使用long,但是会有一些限制,在运行时访问一个稳定的id会被限定(不过目前没有发现有什么限定,测试中直接使用了list的索引); Parcelable:任何Parcelable都可以用作selection的key,如果view中的内容与稳定的content:// uri相关联,就是用uri作为key的类型;(这个还没有试验)
public class StringItemKeyProvider extends ItemKeyProvider<String> {
private List<String> items;
public StringItemKeyProvider(int scope, List<String> items) {
super(scope);
this.items = items;
}
@Nullable
@Override
public String getKey(int position) {
return items.get(position);
}
@Override
public int getPosition(@NonNull String key) {
return items.indexOf(key);
}
}
- 实现ItemDetailsLookup接口,该接口可以接受RecyclerView的Item上发生的MotionEvent事件,我们需要实现其
ItemDetails getItemDetails(@NonNull MotionEvent e)
方法,通过ReyclerView的findChildView(int,int)
方法来判断具体touch的是哪一个Item,强转成我们的ViewHolder类型,调用我们RecyclerView.ViewHolder中的方法来返回一个ItemDetails实例,返回实例的方法是我们自己添加的,ViewHolder中并没有该抽象方法;
public class StringItemDetailsLookup extends ItemDetailsLookup {
private final RecyclerView mRecyclerView;
StringItemDetailsLookup(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
@Nullable
@Override
public ItemDetails getItemDetails(@NonNull MotionEvent e) {
View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
if (holder instanceof StringItemRecyclerViewAdapter.ItemViewHolder) {
return ((StringItemRecyclerViewAdapter.ItemViewHolder) holder).getItemDetails();
}
}
return null;
}
}
以下是返回ItemDetails实例的方法,其中注释的语句是通过继承ItemDetails的子类来创建的。
ItemDetailsLookup.ItemDetails getItemDetails() {
// return new StringItemDetail(getAdapterPosition(),datas.get(getAdapterPosition()));
return new ItemDetailsLookup.ItemDetails() {
@Override
public int getPosition() {
return getAdapterPosition();
}
@Nullable
@Override
public Object getSelectionKey() {
return datas.get(getAdapterPosition());
}
};
}
public class StringItemDetails extends ItemDetailsLookup.ItemDetails {
private int position;
private String item;
public StringItemDetails(int position, String item) {
this.position = position;
this.item = item;
}
@Override
public int getPosition() {
return position;
}
@Nullable
@Override
public Object getSelectionKey() {
return item;
}
}
现在我们准备好了StringItemDetailsLookup
、StringItemKeyProvider
两个类,但是准备好了又怎么用呢?
- 创建SelectionTracker实例,在Activity的OnCreate中加入如下代码(注意是在一个参数的OnCreate中,Android5.0以上两个参数的方法只有在Manifast文件为Activity设置了
android:persistableMode
属性才会调用,同样onSaveInstanceState
、onRestoreInstanceState
也是如此,没注意参数个数导致我浪费 了大半个小时,哭),创建基本的实例,其他的查看源码即可:
mAdapter = new StringItemRecyclerViewAdapter(ITEMS);
mRecyclerView.setAdapter(mAdapter);
mSelectionTracker = new SelectionTracker.Builder<>(
"string-items-selection",
mRecyclerView,
new StringItemKeyProvider(1, ITEMS),
new StringItemDetailsLookup(mRecyclerView),
StorageStrategy.createStringStorage())
//设置可选择的item,这里设置为都可选
.withSelectionPredicate(SelectionPredicates.<String>createSelectAnything())
.build();
mAdapter.setSelectionTracker(mSelectionTracker);
注意上边setSelectionTracker
方法,这个方法是我们在自定义的RecyclerView.Adapter中添加的,目的是将我们创建的SelectionTracker注入到我们的Adpter。
public void setSelectionTracker(SelectionTracker mSelectionTracker) {
this.mSelectionTracker = mSelectionTracker;
}
前边说了,我们在Adapter中添加了getItemDetails
方法,现在我们的基本工作已经完成,程序能正常运行了,但是我们还看不到多选的效果,因为tracker并不能为我们提供选中高亮功能,高亮功能按我们喜欢的方式实现即可,判断是否选中的代码如下(在Adapter的onBindViewHolder方法中):
if (mTracker.isSelected(datas.get(i))) {
viewHolder.tvInfo.setBackgroundColor(Color.parseColor("#80deea"));
} else {
viewHolder.tvInfo.setBackgroundColor(Color.WHITE);
}
- 前边的代码已经足以我们完成RecyclerView多选功能,但是当屏幕配置改变,比如旋转屏幕时,我们已选中的item的选中状态就会消失,所以我们需要将我们的选中状态进行持久化操作,tracker为提供了相应的API方便我们持久化,将数据持久化操作与Activity的生命周期事件进行绑定。在Activity的
onSaveInstanceState
、onRestoreInstanceState
中分别调用tracker的onSaveInstanceState
、onRestoreInstanceState
方法即可,如下:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
mSelectionTracker.onRestoreInstanceState(savedInstanceState);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mSelectionTracker.onSaveInstanceState(outState);
}
后续
我们可以为tracker添加订阅事件或者item单击事件,这样选择状态改变或者item被点击是我们可以添加自己的逻辑,后续多选操作及多选信息我们都可以通过tracker的事件或者方法获得,具体请看参考3的代码或者查看官网SelectionTracker的说明。
最后,必须第一步的操作,Android studio 3.4竟然无法直接搜索recyclerview添加其依赖,搜索结果是androidx版本的,会有冲突,recyclerview-selection倒是可以直接搜索依赖到项目中。
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:recyclerview-selection:28.0.0'