本文原作者: 黄林晴,原文发布于: Android技术圈
本篇文章主要来看在 Compose 中如何使用 Paging 3,这里不得不说一句,在 xml 中使用 Paging 3 和在 Compose 中使用仅有 UI 层代码不同,所以之前定义的接口层、仓库层直接复用代码即可。
Paging 3 的使用
项目搭建
首先,我们新建项目,在 build.gradle 中引入 compose 的 paging 库,这里将网络请求等依赖库一并引入。代码如下所示:
- implementation("androidx.paging:paging-compose:1.0.0-alpha14")
- implementation "com.squareup.retrofit2:retrofit:2.9.0"
- implementation "com.squareup.retrofit2:converter-gson:2.9.0"
- implementation "com.squareup.okhttp3:logging-interceptor:4.9.2"
- implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"
API 接口准备
这里我们已经写好了 RetrofitService 类用于创建网络请求的 service 代码如下所示:
- object RetrofitService {
-
-
-
-
- /**
- * okhttp client
- */
- lateinit var okHttpClient: OkHttpClient
-
-
- /**
- * 主Url地址
- */
- private const val BASEAPI = "https://www.wanandroid.com/";
-
-
-
-
- /**
- * 创建service对象
- */
- fun <T> createService(mClass: Class<T>): T {
- val builder: OkHttpClient.Builder = OkHttpClient.Builder();
- okHttpClient = builder.build()
- val retrofit: Retrofit = Retrofit.Builder()
- .baseUrl(BASEAPI)
- .client(okHttpClient)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- return retrofit.create(mClass) as T
- }
-
-
- }
定义 DataApi 接口,这里我们将方法声明为挂起函数,代码如下所示:
- interface DataApi {
-
-
- /**
- * 获取数据
- */
- @GET("wenda/list/{pageId}/json")
- suspend fun getData(@Path("pageId") pageId:Int): DemoReqData
- }
定义数据源
首先我们来定义数据源 DataSource 继承自 PagingSource,代码如下所示:
- class DataSource():PagingSource<Int,DemoReqData.DataBean.DatasBean>(){
- override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DemoReqData.DataBean.DatasBean> {
- TODO("Not yet implemented")
- }
-
-
- override fun getRefreshKey(state: PagingState<Int,
- DemoReqData.DataBean.DatasBean>): Int? {
- return null
- }
- }
getRefreshKey 方法是新增的,之前没有提到过,这里讲解一下这个方法的用途。
getRefreshKey
getRefreshKey 方法意思是 refresh 时,从最后请求的页面开始请求,null 则请求第一页。
举个例子,请求出错时会调用 refresh 方法加载,如果当前已经请求了第一页到第四页的数据,可以通过设置在 refresh 后会加载第 5 - 8 页的数据,并且前四页的数据都没了。如果 getRefreshKey 返回 null,refresh 后会重新加载第一到第四页的数据,这里我们直接返回 null 即可。
DataSource 为我们自动生成了 load 方法,我们主要的请求操作就在 load 方法中完成。主要代码如下所示:
- class ADataSource : PagingSource<Int, DemoReqData.DataBean.DatasBean>() {
- override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DemoReqData.DataBean.DatasBean> {
-
-
- return try {
-
-
- //页码未定义置为1
- val currentPage = params.key ?: 1
- //仓库层请求数据
- Log.d("请求页码标记", "请求第${currentPage}页")
- val demoReqData = DataRespority().loadData(currentPage)
- //当前页码 小于 总页码 页面加1
- val nextPage = if (currentPage < demoReqData.data?.pageCount ?: 0) {
- currentPage + 1
- } else {
- //没有更多数据
- null
- }
-
-
- LoadResult.Page(
- data = demoReqData.data!!.datas!!,
- prevKey = null,
- nextKey = nextPage
- )
-
-
- } catch (e: Exception) {
- if (e is IOException) {
- Log.d("测试错误数据", "-------连接失败")
- }
- Log.d("测试错误数据", "-------${e.message}")
- LoadResult.Error(throwable = e)
- }
-
-
- }
-
-
- override fun getRefreshKey(state: PagingState<Int, DemoReqData.DataBean.DatasBean>): Int? {
- return null
- }
-
-
- }
接下来我们看下 DataRespority 仓库层的代码,代码比较简单,如下所示:
- class DataRespority {
-
-
- private var netWork = RetrofitService.createService(
- DataApi::class.java
- )
-
-
- /**
- * 查询护理数据
- */
- suspend fun loadData(
- pageId: Int
- ): DemoReqData? {
- return try {
- netWork.getData(pageId)
- } catch (e: Exception) {
- //在这里处理或捕获异常
- null
- }
-
-
- }
- }
创建 viewmodel
创建 viewModel 对象,并创建 pager 对象从而调用 PagingSource 方法,代码如下所示:
- class MainActivityViewModel : ViewModel() {
-
-
- /**
- * 获取数据
- */
- fun getData() = Pager(PagingConfig(pageSize = 1)) {
- DataSource()
- }.flow
- }
接下来我们主要看 UI 层的代码如何实现。
实现 UI 层代码
View 层数据请求并将结果显示在 View 上
- val mainViewmodel: MainActivityViewModel = viewModel()
- 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 函数,代码如下所示:
- @Composable
- fun Message(data: DemoReqData.DataBean.DatasBean?) {
- Card(
- modifier = Modifier
- .background(Color.White)
- .padding(10.dp)
- .fillMaxSize(), elevation = 10.dp
- ) {
- Column(modifier = Modifier.padding(10.dp)) {
- Text(
- text = "作者:${data?.author}"
- )
- Text(text = "${data?.title}")
- }
- }
- }
运行程序,结果下图所示。
监听 Paging 3 状态
这里我们以 refresh 时加载为例,代码如下所示:
- if (data.loadState.refresh is LoadState.Loading) {
- Log.d(TAG, "正在加载")
- } else if (data.loadState.refresh is LoadState.Error) {
- when ((data.loadState.refresh as LoadState.Error).error) {
- is IOException -> {
- Log.d(TAG, "网络未连接,可在这里放置失败视图")
- }
- else -> {
- Log.d(TAG, "网络未连接,其他异常")
- }
- }
- }
断开网络,运行程序,打印如下图所示:
这里放置失败视图我们就不再演示了,如果想在失败时刷新的话,直接调用 data.refresh 即可。
完整代码如下所示:
- @Composable
- fun Greeting() {
- val mainViewmodel: MainActivityViewModel = viewModel()
- val data = mainViewmodel.getData().collectAsLazyPagingItems()
- Column {
- LazyColumn() {
- items(items = data) { item ->
- Message(data = item)
- }
-
-
- val TAG = "加载状态"
-
-
- if (data.loadState.refresh is LoadState.Loading) {
- Log.d(TAG, "正在加载")
- } else if (data.loadState.refresh is LoadState.Error) {
- when ((data.loadState.refresh as LoadState.Error).error) {
- is IOException -> {
- Log.d(TAG, "网络未连接,可在这里放置失败视图")
- }
- else -> {
- Log.d(TAG, "网络未连接,其他异常")
- }
- }
- }
- }
- }
-
-
-
-
- }
-
-
- @Composable
- fun Message(data: DemoReqData.DataBean.DatasBean?) {
- Card(
- modifier = Modifier
- .background(Color.White)
- .padding(10.dp)
- .fillMaxSize(), elevation = 10.dp
- ) {
- Column(modifier = Modifier.padding(10.dp)) {
- Text(
- text = "作者:${data?.author}"
- )
- Text(text = "${data?.title}")
- }
- }
-
-
- }
这样我们就实现了,在 Compose 中使用分页库的功能了。
源码地址已上传:
https://github.com/huanglinqing123/ComposePagingDemo
长按右侧二维码
查看更多开发者精彩分享
"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。
点击屏末 | 阅读原文 | 即刻报名参与 "开发者说·DTalk"