使用 BRVAH 更简单的操作 RecyclerView
一、概述
1.1 BRVAH
- BRVAH 也就是 BaseRecyclerViewAdapterHelper 的缩写,是目前 GitHub 上拥有 23.8k stars 的一个很受欢迎的开源项目,旨在精简我们对 Recyclerview 的操作。
- Recyclerview 作为安卓开发者最常用的控件之一,功能十分强大,可以显示动态的列表、网格以及瀑布流数据。但是相应的使用难度也就有所上升,它不能像普通控件一样拖到界面上就可以使用了,还需要专门设置数据适配器、布局管理器等等。
1.2 BaseQuickAdapter
- 在日常开发中我们常用的 Recyclerview 适配器有两种:
Recyclerview.Adapter<VH>
和ListAdapter<T,VH>
,后者扩展性更强一些,一般简单的需求使用前者,略微复杂的需求一般会使用后者。 - 不过,虽然原生的适配器能实现大部分功能,但是像 item 的点击事件、适配器动画、头部和尾部的添加、item 的拖拽等等需要我们自己花费不少的精力去实现,原生并未提供相应的 API 支持。
BaseQuickAdapter<T,VH>
是 BRVAH 中非常重要的一个适配器,将上面说的大部分功能都已经做好了,我们只需要使用对应的 API 进行非常简单的操作即可实现强大的扩展功能。
二、简单使用
2.1 导入依赖
- 这里只介绍 4.0+ 版本的使用,遵循 Kotlin First 原则,和之前的版本肯定会有很多不同,之前版本的学习可以去 BRVAH官网 查看详细文档。
- 在 build.gradle(app) 下添加依赖:
1 | dependencies { |
2.2 创建实体类
- 我们这里实现一个图片 + 文字的简单列表条目的数据呈现,创建一个实体类 Emoji:
1 | data class Emoji( |
2.3 创建布局和适配器
- 创建一个 item 的布局,包含一张图片和两个文字,很简单就不给出了。
- 创建一个继承自
BaseQuickAdapter<T,VH>
的适配器,需要实现其必要方法 onBindViewHolder 和 onCreateViewHolder:
1 | class EmojiAdapter : BaseQuickAdapter<Emoji, EmojiAdapter.VH>() { |
- 这里我们自己写了一个 ViewHolder,其实不写也是可以的,如果你的Adapter特别简单,不想用ViewBinding,可以使用QuickViewHolder:
1 | class TestAdapter : BaseQuickAdapter<Status, QuickViewHolder>() { |
2.4 设置适配器
- 最后在 Activity 或者 Fragment 中,将 Recyclerview 的适配器和布局管理器设置好即可:
1 | val adapter = EmojiAdapter() |
- 效果也就出来了:
三、BaseQuickAdapter
- 看上面适配器的代码可以发现,代码量几乎是没什么变化的,那这个
BaseQuickAdapter
好在哪里呢? - 首先我们能感觉到 BaseQuickAdapter<T,VH> 好像是个加强版的 ListAdapter<T,VH>,因为他也是通过 submitList 来提交数据,只是少了一个 DiffUtil.ItemCallback
?。 - 其实不是的,我们看源码能发现,BaseQuickAdapter 其实是继承自 RecyclerView.Adapter 的,submitList 只是其实现的一个扩展方法而已,如果我们要实现自定义的比较逻辑来提高性能,需要用另一个适配器:BaseDifferAdapter
- 参考源码:
1 | abstract class BaseQuickAdapter<T, VH : RecyclerView.ViewHolder>( |
- 除了上面提到的两个适配器,BRVAH 还提供了很多个其他的适配器应对不同的场景,但是说得越多越乱,复杂的功能就先不看,我们先来体验一下
BaseQuickAdapter
为我们提供了哪些方面的扩展功能。
3.1 空视图
- v4 版本官网中对于 BaseQuickAdapter 是这么描述的:
- 也就也是说,目前 v4 版本的 BaseQuickAdapter 作为最基本且纯粹的适配器,只提供空布局、数据操作、点击事件三个功能了。
- 我们先来体验一下”空视图”功能,非常简单:
- 首先,是否使用空布局(默认false),需要将其打开:
1 | adapter.isEmptyViewEnable = true |
- 然后有两种方式设置空布局:
- 使用Layout Id:
1 | adapter.setEmptyViewLayout(context, R.layout.loading_view) |
- 使用 View
1 | mAdapter.emptyView = view |
3.2 数据操作
- 设置数据集合
1 | adapter.submitList(list) |
- 修改某一位置的数据
1 | //修改index为1处的数据 |
- 新增数据
1 | // 尾部新增数据 |
- 删除数据
1 | // 删除数据 |
- 交换数据位置(仅仅是这两个数据的位置交换)
1 | // 交换两个位置的数据,只有1和3会变化 |
- 移动数据位置(注意和 swap 的区别)
1 | // 交换两个位置的数据,1跑到3的位置,原来的2和3都会往前移一位,3后面的不会发生变化,这个必须使用可变的集合 |
- 获取Item数据的索引
1 | // 如果返回 -1,表示不存在 |
- 根据索引,获取Item数据
1 | // 如果返回 null,表示没有数据 |
3.3 动画
- 动画是指 item 出现或者消失时候的动画效果,内部内置了5种默认动画
1 | /** |
- 自定义动画 需继承 ItemAnimator 实现自定义动画
1 | class CustomAnimation1 : ItemAnimator { |
- 重写动画执行操作
1 | class TestAdapter : BaseQuickAdapter<Status, QuickViewHolder>() { |
- 是否打开动画
1 | adapter.animationEnable = true |
3.4 点击事件
使用 BaseQuickAdapter 后,我们不再需要自己写接口实现 item 的各种触摸事件
item 点击事件
1 | adapter.setOnItemClickListener { adapter, view, position -> |
- item 长按事件
1 | adapter.setOnItemClickListener { adapter, view, position -> |
- item 长按事件
1 | adapter.setOnItemLongClickListener { adapter, view, position -> |
- item 子控件点击事件
1 | // 需要传递控件 id |
- item 子控件长按事件
1 | // 需要传递控件 id |
3.5 拖拽功能
- 这个功能的实现相对复杂,但是相较于原生编写,代码量任然有所降低。
- 实现 DragAndSwipeDataCallback 接口
- adapter 实现 DragAndSwipeDataCallback 接口,并重写 dataSwap() 和 dataRemoveAt() 方法
1 | class EmojiAdapter : BaseQuickAdapter<Emoji, EmojiAdapter.VH>() , DragAndSwipeDataCallback { |
- 创建 QuickDragAndSwipe 对象
1 | val quickDragAndSwipe = QuickDragAndSwipe().apply { |
- 附加到 RecyclerView 中
1 | quickDragAndSwipe.attachToRecyclerView(binding.recyclerView).apply { |
- 看看最终实现的效果: