最近在项目中,使用到了Paging3作为分页组件开发分页功能,在使用的过程中,还是碰到了很多问题,这里简单汇总一下,后续会出一期源码分析
Paging3的使用问题汇总
1 添加Footer或者Header
在Paging3中,已经支持动态添加头部或者尾部,实现上拉加载和下拉刷新;经常在项目中碰到的一个场景就是,当分页数据加载完成后,需要显示一个已经加载完成的底部标签,那么就可以给列表添加一个尾部
在Paging3中使用RecyclerView加载分页数据,就得使用PagingDataAdapter作为适配器,该适配器有3个api用于添加头部或者尾部,其中添加尾部使用withLoadStateFooter
这里就有一个问题,在官网文档中也没有提及,其他博客中也写的非常笼统,
adapter = PagingListAdapter(requireContext())
val withLoadStateFooter = adapter.withLoadStateFooter(FootAdapter(requireContext()))
//这里返回的adapter为ConcatAdapter
binding.rvContent.adapter = withLoadStateFooter
调用withLoadStateFooter后返回的是ConcatAdapter,就是将列表主体和尾部结合的适配器,给RecyclerView设置适配器不能再设置之前的PagingListAdapter,而是需要设置这个ConcatAdapter,不然没有效果
2 LoadStateAdapter的使用
在Paging3中,提供了另一个状态适配器LoadStateAdapter,通过判断数据加载的状态,来控制UI的展示或者隐藏,来完成交互
class FootAdapter(
val context: Context
) : LoadStateAdapter<FooterViewHolder>() {
override fun onBindViewHolder(holder: FooterViewHolder, loadState: LoadState) {
when(loadState){
is LoadState.NotLoading -> {
holder.binding.tvFinish.visibility = View.VISIBLE
}
is LoadState.Loading ->{
holder.binding.tvFinish.visibility = View.GONE
}
else -> {
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): FooterViewHolder {
return FooterViewHolder(
LayoutPagingFootBinding.bind(
LayoutInflater.from(context).inflate(R.layout.layout_paging_foot,parent,false)
)
)
}
/**
* 重写该方法,否则底部LoadStatus不展示,闪过
* By default, both [LoadState.Loading] and [LoadState.Error] are presented as adapter items,
* other states are not. To configure this, override [displayLoadStateAsItem].
*/
override fun displayLoadStateAsItem(loadState: LoadState): Boolean {
return loadState is LoadState.Loading ||
loadState is LoadState.Error ||
(loadState is LoadState.NotLoading && loadState.endOfPaginationReached);
}
override fun getStateViewType(loadState: LoadState): Int {
return LoadStatus.END.ordinal
}
}
class FooterViewHolder(
var binding: LayoutPagingFootBinding
) : RecyclerView.ViewHolder(binding.root)
问题1:Footer一闪而过,只有在加载的时候显示,加载完成之后,就不显示了
在LoadStateAdapter中有一个方法,displayLoadStateAsItem,官方的注释中写道
默认情况下,[LoadState.Loading] 和 [LoadState.Error] 都显示为适配器项,其他的状态不显示,如果想要其他的状态显示,那么就重写这个方法
所以,当列表加载完成之后,尾部不显示的原因就在这里,所以需要重写,displayLoadStateAsItem,当列表加载到底部的时候,也需要展示为适配器的选项
问题2:如果RecyclerView设置了GridLayoutManager,如何让Footer独占一行?
binding.rvContent.layoutManager =
GridLayoutManager(requireContext(), 3, GridLayoutManager.VERTICAL, false)
.apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup(){
override fun getSpanSize(position: Int): Int {
return if(withLoadStateFooter.getItemViewType(position) == LoadStatus.END.ordinal) spanCount else 1
}
}
}
默认情况下,如果GridLayoutManager设置了列数超过1,那么显示的Footer也只是占据1个item的位置,这种情况下就不符合设计的需求,要求当显示Foot的时候,需要独占一行。
这个时候,需要设置GridLayoutManager的spanSizeLookup属性,判断当列表到尾部的时候,将spanSizeLookup设置为一行(也就是列数)
如何判断是否到了底部,就需要通过ConcatAdapter判断item的类型,在LoadStateAdapter中重写getStateViewType, 因为是Foot,所以就是一种类型直接返回,ConcatAdapter在判断类型时,如果是Foot,就设置spanSizeLookup为spanCount