preface
1. Must PagingDataAdapter be inherited to use Paging? 2. Use AsyncPagingDataDiffer to implement the Paging function example 3. Furthermore, the decorative mode is used to realize the Paging function
If you find this article helpful, please give it a thumbs up. Thank you
usePaging
Must inheritPagingDataAdapter
?
Paging3 has recently released a beta version, I believe that a few months should be officially released using Paging3 can be more convenient to simplify the loading of more logic, can be more easily in RecyclerView gradually properly load data
Paging3 must be inherited from PagingAdapter. This is actually wrong. If we click on the source of PagingDataAdapter, You can see that AsyncPagingDataDiffer does most of its work
The main component of Paging library in the interface layer is PagingDataAdapter, which is a RecyclerView adapter to process Paging data. Alternatively, you can use the accompanying AsyncPagingDataDiffer component to build your own custom adapters.
In actual development, our Adapter often has its own base class, and it is not convenient to inherit PagingDataAdapter, so it is a more practical choice to build your own custom adapter based on AsyncPagingDataDiffer in actual development
useAsyncPagingDataDiffer
Custom adapter
1. Look at the original firstBaseAdapter
We usually already have Adapter base classes in our projects, so let’s define a simple one
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(a): Int {
return mDataList.size
}
}
Copy the code
The BaseAdapter we define here is very simple, just for the purpose of simulation, mainly support setDataList functionality, and nothing else
2. CustomPagingAdapter
We can customize the PagingAdapter to realize the Paging function and inherit it from the BaseAdapter without affecting the original function
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 {
// Listen for data and assign a value to BaseAdapter after loading successfully
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) {
// This step is necessary because Paging is preloaded via getItem
differ.getItem(position)
}
}
Copy the code
The PagingAdapter mainly does the following: 1. Define differ for subsequent submitList updates to use 2. Provide the submitList method to update data 3. Call getItem in onBindViewHolder. This step is essential because Paging preloads the next page through getItem. Listen to the loading status and call setDataList to assign a value to BaseAdapter after the loading is successful
3. Use customPagingAdapter
When we want to develop a new Adapter, we simply inherit the 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)
}
}
}
Copy the code
DemoAdapter, which originally inherits from BaseAdapter, almost does not need to modify the code, but inherits from PagingAdapter, which realizes the enhancement of the function and supports the automatic preloading function
Achieve the decorative mode to achieve the Paging function
The AsyncPagingDataDiffer custom adapter inherits from BaseAdapter and implements enhancements. What’s wrong with this?
1. What’s wrong with inheritance?
The methods used above are essentially inherited
- Increased levels of inheritance affect the maintainability of code
We are inBaseAdapter
withDemoAdapter
In between, another layer is addedPagingAdapter
If we add new features later, we may need to add new onesAdapter
Base classes, which are difficult to maintain - The implementation of the subclass depends on the implementation of the parent class, destroying the encapsulation of the class. If we want to figure out what methods and attributes the subclass has, we must follow up to read the code of the parent class, and the implementation of the subclass depends on the implementation of the parent class. If the parent class is modified, it will affect the logic of all the subclasses and bring unpredictable bugs
2. Use decoration
1. Define one firstPagingWrapAdapter
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(a): Int {
return innerAdapter.itemCount
}
}
Copy the code
PagingWrapAdapter mainly do the following work 1. Inherit in RecyclerView.Adapter, and achieve several default methods 2. 2. Pass in an innerAdapter and implement the above methods by its agent
The UML diagram is as follows:
2. Call method
private val mAdapter by lazy {
val readAdapter = Demo2Adapter()
PagingWrapAdapter<NewsBean.StoriesBean, Demo2Adapter.ViewHolder>(readAdapter) {
readAdapter.setDataList(it)
}
}
Copy the code
As you can see, the original Adapter does not need any modification, just need to pass into the PagingWrapAdapter, to implement the preload function
conclusion
Since adapters in our real projects often already have base classes, it is not convenient to inherit PaginDataAdapter and build custom adapters based on AsyncPagingDataDiffer is a better choice
Inheritance will also bring problems such as maintainability. A better choice is to use decoration mode to enhance functions to achieve the goal of realizing Paging function without modifying the original code
Show Me The Code
All code in this article is visible: PagingAdapter
This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign