前言
本文主要包括以下内容
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 春招闯关活动」, 点击查看活动详情