官方文档:https://developer.android.google.cn/topic/libraries/architecture/paging/v3-overview?hl=zh-cn
使用
- 定义数据流 PagingDataSource
/**
* 网络返回数据定义 eg
*/
data class ListItemBean(val data: String? = null)
/**
* 数据流获取定义
* @param extraParams 定义自己需要传递的参数 Any?自己需要什么类型就传什么类型
*/
class ListDataPagingSource(private val extraParams: Any?) : PagingSource<Int, ListItemBean>() {
/**
* getRefreshKey 请求刷新 或失败重试时的 LoadParams.key
*/
override fun getRefreshKey(state: PagingState<Int, ListItemBean>): Int? {
Log.e(">>>>>", "getRefreshKey: --> ${state.pages.size} ${state.anchorPosition}")
//失败或刷新时 得到当前要重新获取的页
return null //直接返回null也行 就是刷新 这里返回数据不对容易造成刷新数据混乱
}
/**
* 加载数据
*/
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ListItemBean> {
Log.e(">>>>>", "load: --> pageNumber:${params.key} pageSize:${params.loadSize}")
//检查自己传递的请求参数
if (extraParams == null)
return LoadResult.Error(Exception("参数缺失,extraParams is null"))
return try {
//当前请求页数信息
val targetPage = params.key ?: 1
//网络请求过程 这里调用自己的api来获取数据
val dataList = withContext(Dispatchers.Default) {
//模拟网络请求延时
delay(Random.nextLong(400L, 1000L))
//模拟数据
mutableListOf<ListItemBean>().apply {
for (i in 0 until params.loadSize) {
add(ListItemBean("第${targetPage}页,item${i + 1}"))
}
}
}
//返回数据
LoadResult.Page(
data = dataList,
prevKey = (targetPage - 1).let {//上一页 实际按接口返回分页信息处理
if (it > 0) it else null
},
nextKey = (targetPage + 1).let {//下一页(模拟只有5页数据) 实际按接口返回分页信息处理
if (it <= 5) it else null
}
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
- 定义viewModel中 Flow<PagingData<T>>
class PagingViewModel : ViewModel() {
/**
* 获取数据流
* @param params 获取接口数据需要的参数 这里是通过PagingSource的构造方法传递过去
*/
fun getListData(params: String?): Flow<PagingData<ListItemBean>> {
return Pager(
config = PagingConfig(
pageSize = 10, //每页加载个数 对应PagingSource的load()方法中的 params.loadSize
initialLoadSize = 10, //第一次加载个数 不配置默认第一次加载分页个数的3倍 直接和分页数一致也许
//enablePlaceholders = true //占位是否开启
),
// initialKey = null,
pagingSourceFactory = {
ListDataPagingSource(params)
}
)
.flow
.cachedIn(viewModelScope)
}
}
- 定义PagingDataAdapter
/**
* pagingAdapter
*/
class SourceListAdapter : PagingDataAdapter<ListItemBean, RecyclerView.ViewHolder>(
diffCallback = object : DiffUtil.ItemCallback<ListItemBean>() { //用来做数据比较判断界面刷新
override fun areItemsTheSame(oldItem: ListItemBean, newItem: ListItemBean): Boolean {
return oldItem.hashCode() == newItem.hashCode() //比较唯一标识 demo随便写的
}
override fun areContentsTheSame(oldItem: ListItemBean, newItem: ListItemBean): Boolean {
return oldItem == newItem //比较内容 最好重写对象的equals方法来做比较
}
}) {
//点击事件回调函数
private var clickCallback: ((Int, ListItemBean) -> Unit)? = null
//设置点击事件
fun addItemClickListener(listener: (Int, ListItemBean) -> Unit) {
this.clickCallback = listener
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
getItem(position)?.let { item ->
holder.itemView.findViewById<TextView>(R.id.content).text = item.data ?: "***"
//点击回调
holder.itemView.setOnClickListener {
clickCallback?.invoke(position, item)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
//ViewHolder图方便 实际项目还是继承之后重写一个
return object : RecyclerView.ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_paging_list, parent, false)
) {}
}
}
- 使用
<--下拉刷新事件用SwipeRefreshLayout监听处理-->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/img_back">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcv_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
class PagingActivity : BaseActivity<ActivityPagingBinding>(ActivityPagingBinding::inflate) {
private val viewModel by viewModels<PagingViewModel>()
private var listAdapter: SourceListAdapter? = null
override fun initView() {
listAdapter = SourceListAdapter()
mBinding.rcvList.apply {
layoutManager = LinearLayoutManager(this@PagingActivity, RecyclerView.VERTICAL, false)
adapter = listAdapter
}
}
override fun initEvent() {
mBinding.refreshLayout.setOnRefreshListener {
//下拉刷新
listAdapter?.refresh()
}
listAdapter?.addItemClickListener { position, itemBean ->
//item点击
Log.e(">>>>>", " --> click $position ${itemBean.toString()}")
}
}
override fun initData() {
lifecycleScope.launch {
viewModel.getListData("user Get Service Data Params").collectLatest { //Flow<PagingData>
//关闭刷新动画
mBinding?.refreshLayout?.isRefreshing = false
//数据展示
listAdapter?.submitData(it)
}
}
}
override fun releaseData() {
listAdapter = null
}
}
以上已实现使用paging加载网络数据展示,下拉刷新和上拉自动加载更多的功能
- 当然也可以增加上拉加载的提示footer
- 定义加载提示LoadStateAdapter
class SimpleLoadStateAdapter : LoadStateAdapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, loadState: LoadState) {
if (loadState.endOfPaginationReached) {
(holder.itemView as TextView).text = "已加载全部数据~"
} else {
(holder.itemView as TextView).text = "loading~"
}
}
/**
* 显示加载试图的时机
*/
override fun displayLoadStateAsItem(loadState: LoadState): Boolean {
return super.displayLoadStateAsItem(loadState)
//这个加上才会回调加载完全部数据
|| (loadState is LoadState.NotLoading && loadState.endOfPaginationReached)
}
override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
): RecyclerView.ViewHolder {
//自定义加载提示视图
val view = TextView(parent.context).apply {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
setPadding(10.px().toInt())
textSize = 16.px()
setTextColor("#666666".toColorInt())
gravity = Gravity.CENTER
}
return object : RecyclerView.ViewHolder(view) {}
}
}
- 改造RecycleView的Adapter设置 adapter = listAdapter?.withLoadStateFooter(SimpleLoadStateAdapter())
override fun initView() {
listAdapter = SourceListAdapter()
mBinding.rcvList.apply {
layoutManager = LinearLayoutManager(this@PagingActivity, RecyclerView.VERTICAL, false)
//将原本的PagingDataAdapter拼接上加载提示LoadStateAdapter变成 ConcatAdapter设置给recycleView
adapter = listAdapter?.withLoadStateFooter(SimpleLoadStateAdapter())
}
}
-
实际运行效果图:
下拉刷新.png
上拉加载.png
加载完全部数据.png
- 更多的使用就自己慢慢摸索了