前言
大约在两年前,写了一篇Jetpack 系列之Paging3,看这一篇就够了~,本篇文章主要来看,在Compose中如何使用Paging3,这里不得不说一句,在xml中使用Paging3和在Compose中使用仅有UI层代码不同,所以之前定义的接口层、仓库层直接复用直接的代码即可。
Paging3的使用
项目搭建
首先,我们新建项目,在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接口准备
API接口我们使用 「鸿洋」大佬?「玩Android」中的查询每日一问接口 :https://wanandroid.com/wenda/list/1/json
这里我们已经写好了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
???}
}
这里具体的含义已经在上篇Paging3文章中讲的很详细了,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}")
????????}
????}
}
运行程序,结果下图所示。?
监听Paging3状态
这里我们以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中使用分页库的功能了。
源码地址已上传
源码地址:GitHub - huanglinqing123/ComposePagingDemo: Compose中使用Paging3分页库示例
|