Paging3的使用踩坑记录

一、Paging3介绍

Paging3是jetpack推出的一个分页加载库,用于方便开发者实现分页加载功能,支持显示加载状态,重试机制,支持协程与RxJava结合使用,相对于传统的分页加载方案,我们不需要关注recyclerview的滑动状态,然后根据状态去实时请求接口,所有相关的判断逻辑,Paging3已经在内部为我们做好了实现,我们只需要实现Paging3的提供的抽象方法,即可实现分页加载功能

二、Paging3的依赖添加

// Paging3默认使用协程,如果不需要使用RxJava,则只引入这一个依赖即可
implementation 'androidx.paging:paging-runtime:3.1.1'

// 如果需要使用RxJava,则需要在引入这个依赖
implementation "androidx.paging:paging-rxjava2:3.1.1"

三、Paging3的使用

Paging3的使用需要关注三个类:

1、PagingDataAdapter

使用方法与RecyclerView的adapter几乎一致,需要注意的是,PagingDataAdapter默认添加了DiffUtil,需要我们手动实现DiffUtil的对比方法

2、Pager

Paging3提供的封装工具,提供配置分页参数,数据请求形式,页面加载逻辑等配置操作

3、SourceFactory

我们需要实现此类,用于实现数据的请求与加载逻辑

/**
 * 时间:2022/5/29 00:33 
 * 作者:菜籽
 * 备注:Paging3的数据适配器,需要实现DiffUtil方法
 */

class AdapterPaging3 : PagingDataAdapter<ItemPaging3, AdapterPaging3.ViewHolder>(itemComparator) {

    companion object {

        val itemComparator = object : DiffUtil.ItemCallback<ItemPaging3>() {
            /**
             * 用来判断新旧条目是否一样,确定是否需要刷新
             * 只有当此方法返回true时,才会执行下面的方法
             * 如果此方法返回false,则下面的方法不会执行
             * 举个例子:当前item的布局没有发生变化,只是布局里面的数据发生了变化,
             *          比如字符串从AAA变成了BBB,则这里返回true,表示不需要重绘当前item的布局
             *
             */
            override fun areItemsTheSame(oldItem: ItemPaging3, newItem: ItemPaging3): Boolean {
                return true
            }

            /**
             * 用来确定是否是同一条目是否一样
             * 注意与上面的方法区分
             * 这里返回true时,只会刷新当前item布局中发生变化的那一部分UI,其余地方不需要动
             * 如果这里返回false,则当前item不会刷新,显示内容也不会发生变化
             */
            override fun areContentsTheSame(oldItem: ItemPaging3, newItem: ItemPaging3): Boolean {
                return !TextUtils.equals(oldItem.title, newItem.title)
            }
        }
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view)


    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.tv_title.text = getItem(position)?.title
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_item_paging3, parent, false)
        return ViewHolder(view)
    }

}
val pagingConfig = PagingConfig(
    /**
    * 每页显示的数据数量,
    * 注意这个页,并不等同于我们接口中定义的条数
    * 这个数量的意思是,每一个页面会加载这么多数据
    * 举个例子,如果接口每页返回10条,而这里定义了20条,则Paging3会请求两次接口,
    * 用来拼成这20条数据
    */
    pageSize = 60,

    // 开启占位符,在加载到正确数据之前,显示的item布局
    enablePlaceholders = true,

    // 预刷新的距离,距离最后一个 item 多远时加载数据
    prefetchDistance = 3,

    /**
     * 初始化加载数量,默认为 pageSize * 3
     *
     * internal const val DEFAULT_INITIAL_PAGE_MULTIPLIER = 3
     * val initialLoadSize: Int = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER
     */
    initialLoadSize = 60,

    /**
     * 一次应在内存中保存的最大数据
     * 超出这个数字的数据会被销毁,当页面滑回来时,会重新请求此页面的数据,
     * 滑动加载更多的数据
     */
    maxSize = 200
)
/**
 * 时间:2022/5/29 00:35
 * 作者:菜籽
 * 备注:使用viewModel+协程的形式来请求数据
 */

class Paging3Coroutines {

    private val repository by lazy {
        val config = PagingConfig(pageSize = 20, initialLoadSize = 5, maxSize = 150)
        val sourceFactory = object : PagingSource<Int, ItemPaging3>() {
            /**
             * 官方解释:每当paging想要加载新的数据来代替当前列表时,会发生刷新操作,回调到这个方法
             *
             * 使用场景:
             * 比如说你当前是第三页,然后用户此时回到第二页时,数据发生变化了,不再前面加载好的数据了,
             * 此时就可以在这里返回第二页的索引,它会重新请求第二页的内容用来展示
             *
             * 目前还没有遇到过这种需求
             */
            override fun getRefreshKey(state: PagingState<Int, ItemPaging3>): Int? {
                return null
            }

            /**
             * 我这里定义的为每页显示10条数据
             * 模拟网络加载失败的情况
             */
            override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ItemPaging3> {
                val currentPage = params.key ?: 0
                Log.d("ItemDataSource", "currentPage:$currentPage")
                if (currentPage > 0) {
                    val random = Math.random()
                    delay((random * 3000).toLong())
                }
                val list = mutableListOf<ItemPaging3>()
                for (i in 0..9) {
                    list.add(ItemPaging3("第" + (10 * currentPage + i) + "条"))
                }
                val prevKey = if (currentPage == 0) null else currentPage - 1
                val nextKey = currentPage + 1
                if (!NetStateHelper.isConnect) {
                    return LoadResult.Error(ConnectException())
                }
                if (currentPage == 4) {
                    // nextKey为null表示数据加载到头了
                    return LoadResult.Page(list, prevKey, null)
                }
                return LoadResult.Page(list, prevKey, nextKey)
            }
        }
        Pager(config, pagingSourceFactory = { sourceFactory })
    }

    fun getData() = repository.flow.asLiveData()

}

四、高级用法:

1、显示加载状态:

Paging3支持设置header和footer用来显示当前的加载状态,我们需要声明一个adapter实现LoadStateAdapter来显示加载状态,具体代码如下:

/**
 * 时间:2022/5/28 23:32
 * 作者:菜籽
 * 备注:网络状态的适配器
 */

class LoadStateAdapterPaging3 : LoadStateAdapter<LoadStateAdapterPaging3.LoadStateViewHolder>() {

    class LoadStateViewHolder(view: View) : RecyclerView.ViewHolder(view)

    private var listener: (() -> Unit)? = null

    fun setOnReloadClickListener(listener: () -> Unit) {
        this.listener = listener
    }

    /**
     * loadState 有三种状态
     *
     */
    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        Log.d("ItemDataSource", "loadState:$loadState")
        holder.itemView.progress_bar.visibility = View.VISIBLE
        holder.itemView.tv_state.text = "正在加载中..."
        if (loadState is LoadState.NotLoading) {
            if (loadState.endOfPaginationReached) {
                holder.itemView.progress_bar.visibility = View.GONE
                holder.itemView.tv_state.text = "数据加载完毕"
            } else {
                holder.itemView.tv_state.text = "滑到头了,继续加载"
            }
            return
        }
        if (loadState is LoadState.Error) {
            holder.itemView.tv_state.text = "加载失败,点击重试"
            holder.itemView.progress_bar.visibility = View.GONE
            holder.itemView.setOnClickListener {
                listener?.invoke()
            }
            return
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.load_state_item_paging3, parent, false)
        return LoadStateViewHolder(view)
    }

    /**
     * 是否显示当前的加载状态
     * 默认的是只有 loading中,loading失败时才会显示加载状态
     * 我们可以改掉super的代码,添加一条当加载完成时,也显示加载状态
     */
    override fun displayLoadStateAsItem(loadState: LoadState): Boolean {
        //return super.displayLoadStateAsItem(loadState)
        return loadState is LoadState.Loading || loadState is LoadState.Error || (loadState is LoadState.NotLoading && loadState.endOfPaginationReached)
    }

}

2、与数据展示相结合:

//通过融合两个adapter实现数据适配器与页面加载适配器融合
adapter.withLoadStateFooter(loadAdapter)

3、与RxJava相结合

/**
 * 时间:2022/5/29 00:39 
 * 作者:菜籽
 * 备注:Paging3与RxJava结合使用
 */

class Paging3RxJava {

    private val repository by lazy {

        val config = PagingConfig(pageSize = 20, initialLoadSize = 5, maxSize = 150)

        val sourceFactory = object : RxPagingSource<Int, ItemPaging3>() {
            override fun getRefreshKey(state: PagingState<Int, ItemPaging3>): Int? {
                return null
            }

            override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, ItemPaging3>> {
                val currentPage = params.key ?: 0
                val delay = if (currentPage > 0) {
                    val random = Math.random()
                    random * 3000
                } else {
                    0f
                }.toLong()
                return Single.just("")
                    .delay(delay, TimeUnit.MILLISECONDS)
                    .map {
                        val list = mutableListOf<ItemPaging3>()
                        for (i in 0..9) {
                            list.add(ItemPaging3("第" + (10 * currentPage + i) + "条"))
                        }
                        val prevKey = if (currentPage == 0) null else currentPage - 1
                        val nextKey = currentPage + 1
                        if (!NetStateHelper.isConnect) {
                            return@map LoadResult.Error(ConnectException())
                        }
                        if (currentPage == 4) {
                            // nextKey为null表示数据加载到头了
                            return@map LoadResult.Page(list, prevKey, null)
                        }
                        return@map LoadResult.Page(list, prevKey, nextKey)
                    }
            }
        }
        Pager(config, pagingSourceFactory = { sourceFactory })
    }

    fun getData() = repository.flow.asLiveData()

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Paging3 是一个用于在 Android 应用中实现分页加载数据的开源库。它是 Google 官方发布的最新版本的 Paging 库,相较于以往的版本,Paging3 在实现简单、功能强大和性能优化方面有了很大的改进。 首先,Android Paging3 提供了强大的数据加载和显示机制。它通过将数据分割成固定大小的数据块 (page),并在需要时按需加载和展示数据,实现了无限滚动加载的效果。相较于传统的 RecyclerView 分页加载,Paging3 更加灵活,可以自动处理数据的加载和卸载,无需手动实现判断是否到底部、加载更多等繁琐逻辑。同时,Paging3 还支持局部刷新、数据源无缝替换等操作,让数据的加载和显示更加简单和高效。 其次,Paging3 在性能方面进行了优化。它使用了异步数据加载和显示机制,可以在后台线程上进行数据加载,不会阻塞主线程。同时,Paging3 采用了数据预加载和缓存策略,可以将下一页的数据提前加载到内存中,从而提高用户体验和应用的响应速度。并且,Paging3 还支持数据的持久化存储,可以将加载的数据缓存到本地数据库或文件中,避免了重复加载数据的开销。 最后,Paging3 还提供了丰富的扩展功能和灵活的定制选项。开发者可以自定义数据加载策略、数据源类型、数据显示方式等,以满足不同的业务需求。同时,Paging3 还提供了相关的辅助类和工具方法,帮助开发者更加便捷地实现数据的分页加载和显示。 总结来说,Android Paging3 是一个功能强大、性能优越的分页加载库,可以帮助开发者轻松实现数据的分页加载和显示,提高应用的用户体验和性能表现。无论是处理大量数据的列表页,还是实现无限滚动加载的功能,Paging3 都是一个值得推荐的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值