RecycleView 多选批处理
androidx.recyclerview.selection 实现 RecycleView 的多选批处理
资料来源:
https://developer.android.com/reference/androidx/recyclerview/selection/package-summary
https://developer.android.com/guide/topics/ui/menus#FloatingContextMenu更新
1
20.07.08 初始化
导语
多选批处理一直拖到现在,实在是失策.对底层改动太大,导致了几个恶性 BUG,闲话打住.
实现多选操作,大部分网上教程都是对 Adapter 改造,但是在 Nowakelock 因为复用了 Adapter 这样的改造基本不可行.
不过最终找到了 androidx.recyclerview.selection 这个官方库,资料有点少,但最终还是完成了功能,对 Adapter 的侵入性很小.
导航栏部分交由 ActionMode 配合.
androidx.recyclerview.selection
大致的使用过程
- 实现一个
ItemKeyProvider<K>
,K 代表的是 item 唯一标识的类型,可以是 自定义类型(可通过 Parcelable 序列化), String, Long,因为批量操作都是启用/禁用标志位,Nowakelock 里选择了 String. - 继承并实现 ItemDetailsLookup::getItemDetails.getItemDetails 是提供有关与用户选择关联的项目的信息,传入 MotionEvent 类型的触控事件,返回触控位置对应 item 的 MotionEvent 信息.通常在由触摸的坐标获取到选择 item 对应的 ViewHolder,由 ViewHolder 返回 ItemDetails.
- 在 ViewHolder 返回 ItemDetails 实际上是继承实现
ItemDetailsLookup.ItemDetails<K>
的 getPosition() 和 getSelectionKey() 方法. - 最后是托管到一个 SelectionTracker ,SelectionTracker 通常是在 fragment/activity 中.实例由 SelectionTracker.Builder 生成.
nowakelock 示例
在 BaseItem::getID() 中可以返回 item 的唯一 String 标识,所以选择实现 ItemKeyProvider<String>
1 | class StringKeyProvider(private val adapter: RecycleAdapter) : |
在 ViewHoder 中实现 getItemDetails() 返回 ItemDetails<String>
实例
1 | fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> = |
实现 StringDetailsLookup
1 | class ItemDetailsLookup(private val recyclerView: RecyclerView) : |
在 Adapter 中增加一个 SelectionTracker<String>
类型变量
1 | var tracker: SelectionTracker<String>? = null |
在 fragment/activity 实例化 tracker,实例化 tracker 是 SelectionTracker.Builder,有几个参数:
- selectionId: tracker 的名称,随意的字符串.
- recyclerView: 传入一个 RecyclerView.
- keyProvider: 与类型对应的 ItemKeyProvider 这里是 StringKeyProvider 实例.
- detailsLookup: 与类型对应的 ItemDetailsLookup 这里是 StringDetailsLookup 实例.
- storage: 与 key 类型有关,String 类型即传入 StorageStrategy.createStringStorage().
1 | tracker = SelectionTracker.Builder<String>( |
实例化后传入 adapter.
选中高亮
最直观的反馈是选中后 item 背景高亮.此处可以配置 item 的背景颜色,再设置 view 的 isActivated 完成.
定义一份颜色 item_background,可以通过 view 的 isActivated 切换.
1 |
|
在 View 中设置
1 | android:background="@drawable/item_background" |
为了应付不需要多选的操作,在 ViewHolder 中实现两个 bind,重点是 binding.root.isActivated = isActivated
1 | inner class ViewHolder(var binding: ViewDataBinding, private val handler: BaseHandler) : |
在 onBindViewHolder 中对 tracker 判断应该调用那个 bind.
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { |
绑定长按事件
方式也非常简单,但就是太啰嗦了.
1 | tracker.addObserver( |
与 ActionMode 配合
详情见 menus#context-menu