在 Compose 中使用 Paging 分页库 | 开发者说·DTalk

137974d45bc62646f85da781e35b5e94.jpeg

本文原作者: 黄林晴原文发布于: Android技术圈

本篇文章主要来看在 Compose 中如何使用 Paging 3,这里不得不说一句,在 xml 中使用 Paging 3 和在 Compose 中使用仅有 UI 层代码不同,所以之前定义的接口层、仓库层直接复用代码即可。

e75ae6d3ac1b8559985bc7f6d83bacab.png

Paging 3 的使用

项目搭建

首先,我们新建项目,在 build.gradle 中引入 compose 的 paging 库,这里将网络请求等依赖库一并引入。代码如下所示:

  1. implementation("androidx.paging:paging-compose:1.0.0-alpha14")
  2. implementation "com.squareup.retrofit2:retrofit:2.9.0"
  3. implementation "com.squareup.retrofit2:converter-gson:2.9.0"
  4. implementation "com.squareup.okhttp3:logging-interceptor:4.9.2"
  5. implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"

API 接口准备

这里我们已经写好了 RetrofitService 类用于创建网络请求的 service 代码如下所示:

  1. object RetrofitService {
  2. /**
  3. * okhttp client
  4. */
  5. lateinit var okHttpClient: OkHttpClient
  6. /**
  7. * 主Url地址
  8. */
  9. private const val BASEAPI = "https://www.wanandroid.com/";
  10. /**
  11. * 创建service对象
  12. */
  13. fun <T> createService(mClass: Class<T>): T {
  14. val builder: OkHttpClient.Builder = OkHttpClient.Builder();
  15. okHttpClient = builder.build()
  16. val retrofit: Retrofit = Retrofit.Builder()
  17. .baseUrl(BASEAPI)
  18. .client(okHttpClient)
  19. .addConverterFactory(GsonConverterFactory.create())
  20. .build()
  21. return retrofit.create(mClass) as T
  22. }
  23. }

定义 DataApi 接口,这里我们将方法声明为挂起函数,代码如下所示:

  1. interface DataApi {
  2. /**
  3. * 获取数据
  4. */
  5. @GET("wenda/list/{pageId}/json")
  6. suspend fun getData(@Path("pageId") pageId:Int): DemoReqData
  7. }

定义数据源

首先我们来定义数据源 DataSource 继承自 PagingSource,代码如下所示:

  1. class DataSource():PagingSource<Int,DemoReqData.DataBean.DatasBean>(){
  2. override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DemoReqData.DataBean.DatasBean> {
  3. TODO("Not yet implemented")
  4. }
  5. override fun getRefreshKey(state: PagingState<Int,
  6. DemoReqData.DataBean.DatasBean>): Int? {
  7. return null
  8. }
  9. }

getRefreshKey 方法是新增的,之前没有提到过,这里讲解一下这个方法的用途。

getRefreshKey

getRefreshKey 方法意思是 refresh 时,从最后请求的页面开始请求,null 则请求第一页。

举个例子,请求出错时会调用 refresh 方法加载,如果当前已经请求了第一页到第四页的数据,可以通过设置在 refresh 后会加载第 5 - 8 页的数据,并且前四页的数据都没了。如果 getRefreshKey 返回 null,refresh 后会重新加载第一到第四页的数据,这里我们直接返回 null 即可。

DataSource 为我们自动生成了 load 方法,我们主要的请求操作就在 load 方法中完成。主要代码如下所示:

  1. class ADataSource : PagingSource<Int, DemoReqData.DataBean.DatasBean>() {
  2. override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DemoReqData.DataBean.DatasBean> {
  3. return try {
  4. //页码未定义置为1
  5. val currentPage = params.key ?: 1
  6. //仓库层请求数据
  7. Log.d("请求页码标记", "请求第${currentPage}页")
  8. val demoReqData = DataRespority().loadData(currentPage)
  9. //当前页码 小于 总页码 页面加1
  10. val nextPage = if (currentPage < demoReqData.data?.pageCount ?: 0) {
  11. currentPage + 1
  12. } else {
  13. //没有更多数据
  14. null
  15. }
  16. LoadResult.Page(
  17. data = demoReqData.data!!.datas!!,
  18. prevKey = null,
  19. nextKey = nextPage
  20. )
  21. } catch (e: Exception) {
  22. if (e is IOException) {
  23. Log.d("测试错误数据", "-------连接失败")
  24. }
  25. Log.d("测试错误数据", "-------${e.message}")
  26. LoadResult.Error(throwable = e)
  27. }
  28. }
  29. override fun getRefreshKey(state: PagingState<Int, DemoReqData.DataBean.DatasBean>): Int? {
  30. return null
  31. }
  32. }

接下来我们看下 DataRespority 仓库层的代码,代码比较简单,如下所示:

  1. class DataRespority {
  2. private var netWork = RetrofitService.createService(
  3. DataApi::class.java
  4. )
  5. /**
  6. * 查询护理数据
  7. */
  8. suspend fun loadData(
  9. pageId: Int
  10. ): DemoReqData? {
  11. return try {
  12. netWork.getData(pageId)
  13. } catch (e: Exception) {
  14. //在这里处理或捕获异常
  15. null
  16. }
  17. }
  18. }

创建 viewmodel

创建 viewModel 对象,并创建 pager 对象从而调用 PagingSource 方法,代码如下所示:

  1. class MainActivityViewModel : ViewModel() {
  2. /**
  3. * 获取数据
  4. */
  5. fun getData() = Pager(PagingConfig(pageSize = 1)) {
  6. DataSource()
  7. }.flow
  8. }

接下来我们主要看 UI 层的代码如何实现。

81cee4ff9bc18c618d97c3abc3438925.png

实现 UI 层代码

View 层数据请求并将结果显示在 View 上

  1. val mainViewmodel: MainActivityViewModel = viewModel()
  2. val data = mainViewmodel.getData().collectAsLazyPagingItems()

首先我们获取 viewmodel 的示例,这里可以调用 viewModel 函数需要引入 lifecycle-viewmodel-compose 库,代码如下所示:

implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"

通过调用 collectAsLazyPagingItems 方法将结果转为 LazyPagingItems 实例,在 LazyColumn 直接调用即可,代码如下所示:

Column {
        LazyColumn() {
            items(items = data) { item ->
                Message(data = item)
            }
        }
    }

Message 是数据展示页面对应的 compose 函数,代码如下所示:

  1. @Composable
  2. fun Message(data: DemoReqData.DataBean.DatasBean?) {
  3. Card(
  4. modifier = Modifier
  5. .background(Color.White)
  6. .padding(10.dp)
  7. .fillMaxSize(), elevation = 10.dp
  8. ) {
  9. Column(modifier = Modifier.padding(10.dp)) {
  10. Text(
  11. text = "作者:${data?.author}"
  12. )
  13. Text(text = "${data?.title}")
  14. }
  15. }
  16. }

运行程序,结果下图所示。

19af2b4f1322e2143f6ba62082a8be25.gif

71a6aa8f7e2e468b6fa32628af8c2d45.png

监听 Paging 3 状态

这里我们以 refresh 时加载为例,代码如下所示:

  1. if (data.loadState.refresh is LoadState.Loading) {
  2. Log.d(TAG, "正在加载")
  3. } else if (data.loadState.refresh is LoadState.Error) {
  4. when ((data.loadState.refresh as LoadState.Error).error) {
  5. is IOException -> {
  6. Log.d(TAG, "网络未连接,可在这里放置失败视图")
  7. }
  8. else -> {
  9. Log.d(TAG, "网络未连接,其他异常")
  10. }
  11. }
  12. }

断开网络,运行程序,打印如下图所示:

6da40981b785dc68b09acbde2ae7ed05.png

这里放置失败视图我们就不再演示了,如果想在失败时刷新的话,直接调用 data.refresh 即可。

完整代码如下所示:

  1. @Composable
  2. fun Greeting() {
  3. val mainViewmodel: MainActivityViewModel = viewModel()
  4. val data = mainViewmodel.getData().collectAsLazyPagingItems()
  5. Column {
  6. LazyColumn() {
  7. items(items = data) { item ->
  8. Message(data = item)
  9. }
  10. val TAG = "加载状态"
  11. if (data.loadState.refresh is LoadState.Loading) {
  12. Log.d(TAG, "正在加载")
  13. } else if (data.loadState.refresh is LoadState.Error) {
  14. when ((data.loadState.refresh as LoadState.Error).error) {
  15. is IOException -> {
  16. Log.d(TAG, "网络未连接,可在这里放置失败视图")
  17. }
  18. else -> {
  19. Log.d(TAG, "网络未连接,其他异常")
  20. }
  21. }
  22. }
  23. }
  24. }
  25. }
  26. @Composable
  27. fun Message(data: DemoReqData.DataBean.DatasBean?) {
  28. Card(
  29. modifier = Modifier
  30. .background(Color.White)
  31. .padding(10.dp)
  32. .fillMaxSize(), elevation = 10.dp
  33. ) {
  34. Column(modifier = Modifier.padding(10.dp)) {
  35. Text(
  36. text = "作者:${data?.author}"
  37. )
  38. Text(text = "${data?.title}")
  39. }
  40. }
  41. }

这样我们就实现了,在 Compose 中使用分页库的功能了。

源码地址已上传:

https://github.com/huanglinqing123/ComposePagingDemo


长按右侧二维码

查看更多开发者精彩分享

d0d37fe04312e9c5b008432ccefde2b9.png

"开发者说·DTalk" 面向3b71e078e5078c77560ec483f5972bc6.png中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。

637b092903b5c38fbc789cf29b9ebb65.gif 点击屏末 | 阅读原文 | 即刻报名参与 "开发者说·DTalk" 


791b699726f4b489f2043429a0c446bb.png

登录后您可以享受以下权益:

×
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回顶部