前言
本文主要包括以下内容
1.使用Paging必须继承PagingDataAdapter吗?
2.使用AsyncPagingDataDiffer实现Paging功能示例
3.更进一步,利用装饰模式实现Paging功能
如果觉得本文对您有所帮助,请帮忙点赞,谢谢~
使用Paging必须继承PagingDataAdapter吗?
Paging3最近已经发布了beta版本,相信几个月内应该就会正式release了
使用Paging3可以比较方便地简化加载更多逻辑,可以更轻松的在RecyclerView中逐步妥善地加载数据
最近在查阅关于Paging3的资料时,看到有不少文章说使用Paging3必须继承于PagingAdapter,这其实是错误的
如果我们点开PagingDataAdapter的源码,可以看到其实他的主要工作是由AsyncPagingDataDiffer完成的
界面层中的主要
Paging库组件是PagingDataAdapter,它是一种处理分页数据的RecyclerView适配器。 此外,您也可以使用随附的AsyncPagingDataDiffer组件来构建自己的自定义适配器。
由于在实际开发中,我们的adapter往往都已经有了自己的基类了,再继承PagingDataAdapter并不方便
所以实际开发中基于AsyncPagingDataDiffer来构建自己的自定义适配器是一个更加实用的选择
使用AsyncPagingDataDiffer自定义适配器
1.首先看看原有的BaseAdapter
我们的项目中往往已经有了Adapter的基类,我们这里先定义一个简单的
abstract class BaseAdapter<T:DifferData,VH:RecyclerView.ViewHolder>:RecyclerView.Adapter<VH>() {
protected var mDataList = mutableListOf<T>()
fun setDataList(dataList:List<T>){
mDataList = dataList.toMutableList()
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return mDataList.size
}
}
我们这里定义的BaseAdapter非常简单,只做模拟作用
主要就是支持setDataList功能,其他就没有什么了
2.自定义PagingAdapter
我们可以通过我自定义PagingAdapter来实现Paging功能,同时继承于BaseAdapter,并不影响原有功能
abstract class PagingAdapter<T:DifferData,VH:RecyclerView.ViewHolder> : BaseAdapter<T,VH>() {
private val differ = AsyncPagingDataDiffer<T>(
diffCallback =DifferCallback(),
updateCallback = AdapterListUpdateCallback(this),
mainDispatcher = Dispatchers.Main,
workerDispatcher = Dispatchers.Default
)
init {
//监听数据,加载成功后给BaseAdapter赋值
differ.addLoadStateListener {
if (it.append is LoadState.NotLoading) {
val items = differ.snapshot().items
setDataList(items)
}
}
}
suspend fun submitList(pagingData: PagingData<T>) {
differ.submitData(pagingData)
}
override fun onBindViewHolder(holder: VH, position: Int) {
//这一步必不可少,因为Paging就是通过getItem触发预加载的
differ.getItem(position)
}
}
PagingAdapter主要做了以下工作
1.定义differ,供后续submitList后更新数据使用
2.提供submitList方法更新数据
3.在onBindViewHolder中调用getItem,这一步是必不可少的,因为Paging预载下一页就是通过getItem触发的
4.监听加载状态,在加载成功后调用setDataList给BaseAdapter中赋值即可
3.使用自定义的PagingAdapter
当我们要开发新的Adapter时,直接继承PagingAdapter即可
class DemoAdapter:PagingAdapter<NewsBean.StoriesBean,DemoAdapter.ViewHolder>() {
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_news,parent,false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
holder.itemView.run {
val data = mDataList[position]
tv_title.text = data.title
Glide.with(context).load(data.images?.get(0)).into(iv_cover)
}
}
}
原本继承于BaseAdapter的DemoAdapter,几乎不用修改代码,改为继承于PagingAdapter,就实现了功能的增强,支持了自动预加载的功能
实现装饰模式实现Paging功能
上文我们通过AsyncPagingDataDiffer自定义适配器,继承于BaseAdapter,实现了功能的增强,这样做有什么问题?
1.继承方式有什么问题?
上文所用的方法,本质还是重重继承的方式
- 增加了继承层次,影响代码的可维护性
我们在BaseAdapter与DemoAdapter之间,又增加了一层PagingAdapter,如果我们以后要添加新功能时,可能又要增加新的Adapter基类,这是难以维护的 - 子类的实现依赖于父类的实现,破坏了类的封装性 如果我们要搞清楚子类有哪些方法与属性,必须层层跟进阅读父类的代码,同时子类的实现依赖于父类的实现,如果父类修改会影响所有子类的逻辑,会带来不可预知的bug
2.使用装饰方式实现
1.首先定义一个PagingWrapAdapter
class PagingWrapAdapter<T : DifferData, VH : RecyclerView.ViewHolder>(
private val innerAdapter: RecyclerView.Adapter<VH>,
private val callback: ((List<T>) -> Unit)
) : RecyclerView.Adapter<VH>() {
private val differ = AsyncPagingDataDiffer<T>(
diffCallback = DifferCallback(),
updateCallback = AdapterListUpdateCallback(this),
mainDispatcher = Dispatchers.Main,
workerDispatcher = Dispatchers.Default
)
init {
differ.addLoadStateListener {
if (it.append is LoadState.NotLoading) {
val items = differ.snapshot().items
callback.invoke(items)
}
}
}
suspend fun submitList(pagingData: PagingData<T>) {
differ.submitData(pagingData)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return innerAdapter.onCreateViewHolder(parent, viewType)
}
override fun onBindViewHolder(holder: VH, position: Int) {
differ.getItem(position)
innerAdapter.onBindViewHolder(holder, position)
}
override fun getItemCount(): Int {
return innerAdapter.itemCount
}
}
PagingWrapAdapter主要做了以下工作
1.继承于RecyclerView.Adapter,并实现几个默认方法
2.构建'differ',实现Paging功能
2.传入一个innerAdapter,将以上方法的实现由其代理实现
UML图如下所示:
2.调用方式
private val mAdapter by lazy {
val readAdapter = Demo2Adapter()
PagingWrapAdapter<NewsBean.StoriesBean, Demo2Adapter.ViewHolder>(readAdapter) {
readAdapter.setDataList(it)
}
}
可以看出,原有的adapter不需要任何修改,只需要传入PagingWrapAdapter中,即可实现预加载功能
总结
由于我们实际项目中的adapter常常已经有基类了,再去继承PaginDataAdapter并不方便
基于AsyncPagingDataDiffer构建自定义适配器是一个更好的选择
使用继承方式也会带来可维护性等问题,更好的选择是使用装饰模式实现对功能的增强以达到:实现Paging功能并不修改原有代码的目标
Show Me The Code
本文所有代码可见:PagingAdapter
本文正在参与「掘金 2021 春招闯关活动」, 点击查看活动详情