本期内容首要介绍在Compose中运用Paging完结静默上拉加载和下拉改写功用。
Android Studio Giraffe | 2022.3.1 AS更新到Giraffe啦,新的UI视角上简约清爽,咱们能够体验一波。
Compose版别为1.4.3
kotlin版别为1.8.10
paging-compose版别为3.2.0
swiperefresh版别为0.31.5-beta
简述
Compose Paging是由Compose和Paging库结合的一种技术,首要便是协助开发者处理列表的分页加载。首要了解下Paging中有几个重要的人物:
-
PagingSource
用于界说数据的来源和加载办法 -
Pager
用于装备分页的巨细和相关PagingSource
,而且能够经过flow()
将结果转换为Flow<PagingData<T>>
-
LazyPagingItems
它能够从PagingData
中搜集数据值,而且在列表中展现其搜集到的值。
运用
了解了Paging重要的人物之后,咱们直接经过代码示例来了解如安在Compose中去运用它。
界说PagingSource
class HomeArticleDataSource(
private val api: Api
) : PagingSource<Int, HomeArticleEntity.Data>() {
override fun getRefreshKey(state: PagingState<Int, HomeArticleEntity.Data>): Int? {
// 根据preKey和nextKey中找到离anchorPosition最近页面的键值
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, HomeArticleEntity.Data> {
// 界说键值
val currentKey = params.key ?: 0
return try {
val response = api.fetchHomeArticleList(currentKey)
val datas = response.data.datas
LoadResult.Page(
data = datas,
prevKey = if (currentKey == 0) null else currentKey - 1,
nextKey = if (currentKey == response.data.pageCount) null else currentKey + 1
)
} catch (exception: Exception) {
LoadResult.Error(exception)
}
}
}
首要咱们要界说数据的加载办法,HomeArticleDataSource
继承了PagingSource
抽象类,其类内部有两个抽象办法
-
getRefreshKey()
这个办法首要便是在改写时寻找key
值,完结也是很简单,根据前后的键值找到相连的键值; -
load()
办法是比较关键的当地,数据是从WanAndroid的APi中获取,经过LoadResult.Page()
回来,他有三个参数,第一个便是需求回来的数据,第二个则是前一个键值,只需求处理当时键值为0时传null
即可,第三个参数是下一个键值,也需求处理下当时键值为最终页时传null
即可;假如在获取数据时产生异常能够经过LoadResult.Error(exception)
回来。
PagingSource
的逻辑仍是比较清晰的,怎么获取数据源(接口、本地数据库)和管理好获取数据时前后的键值改变。
装备Pager
界说好数据获取的办法之后,此刻就需求界说Pager的装备了,这就得交给Pager
目标来完结
class HomeRepository(
private val api: Api
) {
fun fetchHomeArticleList(): Flow<PagingData<HomeArticleEntity.Data>> {
// 经过Pager.flow回来流目标
return Pager(
config = PagingConfig(pageSize = 20),
pagingSourceFactory = {
HomeArticleDataSource(api)
}
).flow
}
}
Pager
目标有两个参数,其一便是config
参数,用于管理每一页加载的数据巨细,这儿需求和接口对应,避免造成接口数据的混乱;第二个pagingSourceFactory
参数便是提供PagingSource
目标而已,咱们只需求将之前界说好的PagingSource
传入即可。
最终能够经过Pager.flow
将数据转换成Flow<PagingData>
目标。
展现数据
以上操作都完结之后,此刻咱们就能够将获取到的数据显示到LazyColumn
中啦,而且在上滑的过程中简直看不到加载下一页的过程,整体作用十分的丝滑流畅。
ViewModel
中articleList
仍是Flow<PagingData<HomeArticleEntity.Data>>
目标,此刻还不能够直接作用于Compose的LazyColumn
,咱们需求再进行一步转换,经过collectAsLazyPagingItems()
办法转换成LazyPagingItems
目标,这样就能够直接在LazyColumn
中直接展现加载到的数据。最终的作用见下方录屏
经过录屏能够看出,在整个上滑的过程中就看不出来“加载更多”的提示啦。
下拉改写
在Compose中假如想完结下拉改写功用,能够直接运用accompanist-swiperefresh库,依靠如下
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanist_version")
详细运用如上面代码,图片要点标红了四处当地,一个一个的解说下:
-
refresh
表明当时下拉改写状况,默以为false
,它是一个State
状况哦 -
pullRefreshState
目标是用于管理改写状况和改写之后详细的动作,这儿直接调用articleList.refresh()
就能够触发Paging
进行数据的改写动作 -
Modifier.pullRefersh()
表明在哪个可组项上能够触发下拉改写动作 -
PullRefreshIndicator
是一个改写指示器,也便是咱们一般看到的转圈圈的动画
经过上述代码咱们就完结了在Paging中下拉改写数据的作用咯,下面是详细的作用
完整代码
class HomeViewModel(
private val repository: HomeRepository
) : ViewModel() {
private val _state = MutableStateFlow(HomeState())
val state = _state.asStateFlow()
val articleList = repository.fetchHomeArticleList().cachedIn(viewModelScope)
}
data class HomeState(
val articleList: List<HomeArticleEntity.Data> = listOf()
)
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun HotScreen() {
val homeViewModel: HomeViewModel = koinViewModel()
// 经过collectAsLazyPagingItems()搜集数据并转成LazyPagingItems目标,
// 此目标可直接作用于LazyColumn
val articleList = homeViewModel.articleList.collectAsLazyPagingItems()
val refresh by remember {
mutableStateOf(false)
}
val pullRefreshState = rememberPullRefreshState(refresh, {
articleList.refresh()
})
Box(
modifier = Modifier
.fillMaxSize()
.padding(8.dp)
.pullRefresh(pullRefreshState)
) {
LazyColumn(modifier = Modifier
.padding(8.dp), content = {
items(articleList.itemCount) {
val data = articleList[it] ?: return@items
HomeArticleItem(data = data)
}
})
PullRefreshIndicator(refresh, pullRefreshState, Modifier.align(Alignment.TopCenter))
}
}
@Preview
@Composable
fun HomeArticleItem(data: HomeArticleEntity.Data = HomeArticleEntity.Data()) {
Box(modifier = Modifier.fillMaxWidth()) {
Column {
Column(
modifier = Modifier
.fillMaxWidth()
.border(
width = 0.5.dp, color = Color.Gray, shape = RoundedCornerShape(
topStart = 8.dp,
bottomEnd = 8.dp
)
)
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(16.dp))
Text(text = data.title)
Spacer(modifier = Modifier.height(16.dp))
Row {
HomeArticleItemTv(text = "共享人:${data.shareUser.ifEmpty { data.author }}")
Spacer(modifier = Modifier.width(8.dp))
HomeArticleItemTv(text = "分类:${data.chapterName}")
Spacer(modifier = Modifier.width(8.dp))
HomeArticleItemTv(text = "时刻:${data.niceShareDate}")
}
Spacer(modifier = Modifier.height(16.dp))
}
Divider(thickness = 0.5.dp, color = Color.Gray)
Spacer(modifier = Modifier.height(8.dp))
}
}
}
@Composable
fun HomeArticleItemTv(text: String) {
Text(
text = text,
color = MaterialTheme.colorAdapter().value.mainTvColor,
fontSize = 12.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
关于我
我是Taonce,假如觉得本文对你有所协助,帮忙点个赞或许保藏,谢谢~