萌萌の初音
萌萌の初音
发布于 2022-05-16 / 769 阅读
0

Jetpack paging3的使用

1. paging3相关依赖的引入:

ext {
    room_version = "2.4.2"
    paging_version = "3.1.1"
    lifecycle_version = "2.4.1"
}

dependencies {
    //paging3 adapter数据需要返回主线程,绑定生命周期进行加载
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

    //paging3的java和kotlin依赖
    implementation "androidx.paging:paging-common:$paging_version"
    implementation "androidx.paging:paging-runtime:$paging_version"
    implementation "androidx.paging:paging-common-ktx:$paging_version"
    implementation "androidx.paging:paging-runtime-ktx:$paging_version"

    //如果使用了Jetpack room作为本地化数据存储就需要引入这个依赖进行配合
    implementation "androidx.room:room-paging:$room_version"
}

2. adapter的创建

需要继承PagingDataAdapter<T : Any, VH : RecyclerView.ViewHolder>类,并重写DiffUtil.ItemCallback接口进行初始化。

class AlbumAdapter(private val listener:(String) -> Unit): PagingDataAdapter<SystemPhotoBean,BaseViewHolder<ItemAlbumBinding>>(diffCallback) {

    companion object {
        val diffCallback = object : DiffUtil.ItemCallback<SystemPhotoBean>() {
            override fun areItemsTheSame(oldItem: SystemPhotoBean, newItem: SystemPhotoBean): Boolean {
                return oldItem.id == newItem.id
            }

            override fun areContentsTheSame(oldItem: SystemPhotoBean, newItem: SystemPhotoBean): Boolean {
                return oldItem == newItem
            }

        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<ItemAlbumBinding> = BaseViewHolder(ItemAlbumBinding.inflate(LayoutInflater.from(parent.context),parent,false))

//其他的实现...
}

3. 搭配room进行数据加载

room接口的实现(很简单只需要在对应的接口返回PagingSource就行):

@Dao
interface PhotoDAO {
    @Query("select * from sy_photo_info")
    fun getPhotoDataSource(): PagingSource<Int,SystemPhotoBean>
}

之后需要对Pager进行初始化,在内部需要实现PagingConfig参数配置以及将刚刚room接口实现的PagingSource方法进行引用即可。Pager可生成flow也可生成livedata。

//生成flow
fun getPhotoDataSource(): Flow<PagingData<SystemPhotoBean>> = Pager(PagingConfig(pageSize = 20, enablePlaceholders = true, initialLoadSize = 60)) {
        cache.getPhotoDataSource()
    }.flow

//生成livedata
fun getPhotoDataSource(): LiveData<PagingData<SystemPhotoBean>> = Pager(PagingConfig(pageSize = 20, enablePlaceholders = true, initialLoadSize = 60)) {
        photoDAO.getPhotoDataSource()
    }.liveData

PagingConfig内部有pageSize、prefetchDistance、enablePlaceholders、initialLoadSize、maxSize五个参数;
pageSize:每页显示的数据的大小;
prefetchDistance:预刷新的距离,距离最后一个 item 多远时加载数据;
enablePlaceholders:是否显示占位符;
initialLoadSize:初始化加载数量;
maxSize:最大数据;

4. 将Pager绑定在adapter上

adapter绑定在list不用多讲,adapter绑定Pager有两种方式,submitData提供带Lifecycle方法和suspend fun协程方法;

//方法一:
lifecycle.coroutineScope.launch {
    viewModel.photoList.collectLatest {
        adapter.submitData(it)
    }
}

//方法二:
viewModel.photoList.collectLatest {
    adapter.submitData(this.lifecycle, it)
}

5. 网络数据的加载:

需要自己实现PagingSource类的处理逻辑,其他实现跟以上教程没有太多区别。

Pager(PagingConfig(pageSize)) {
        object : PagingSource<Int, LiveCoursesBean>() {
            var pageNum: Int = 1
            override fun getRefreshKey(state: PagingState<Int, LiveCoursesBean>): Int? {
                return state.anchorPosition?.let { anchorPosition ->
                    state.closestPageToPosition(anchorPosition)?.prevKey
                }
            }
            override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LiveCoursesBean> {
                return try {
                    //你的api网络请求数据
                    val data = api.getListData(pageNum, pageSize).data
                    //根据你后台返回的数据自行判断
                    when {
                        data != null && data.list.size > 0 && pageNum <= data.pageNum -> {
                            pageNum++
                            LoadResult.Page(data = data.list, prevKey = null, nextKey = pageNum)
                        }
                        else -> LoadResult.Page(data = arrayListOf(), prevKey = null, nextKey = null)
                    }
                } catch (e: Exception) {
                    LoadResult.Error(e)
                }
            }
        }
}.flow

LoadResult.Page在有数据、没有报错的情况下将你的数据进行返回,list就会自行呈现数据。
LoadResult.Error在报错情况下进行调用,在adapter中可以实现状态接口进行监听。

adapter监听:

//用于下拉刷新组件的配合使用
adapter.addLoadStateListener {
    layout.refresh.isRefreshing = it.refresh is LoadState.Loading
}

it.refresh为adapter的状态,通过对refresh进行判断来执行不同的操作和相关UI的显示。

6. 数据的刷新与出错重载

adapter.refresh()//重新从第一页加载
adapter.retry()//从加载失败的地方再次加载