本期内容首要介绍在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中啦,而且在上滑的过程中简直看不到加载下一页的过程,整体作用十分的丝滑流畅。

Compose中结合Paging实现上拉丝滑加载和下拉刷新

Compose中结合Paging实现上拉丝滑加载和下拉刷新

ViewModelarticleList仍是Flow<PagingData<HomeArticleEntity.Data>>目标,此刻还不能够直接作用于Compose的LazyColumn,咱们需求再进行一步转换,经过collectAsLazyPagingItems()办法转换成LazyPagingItems目标,这样就能够直接在LazyColumn中直接展现加载到的数据。最终的作用见下方录屏

经过录屏能够看出,在整个上滑的过程中就看不出来“加载更多”的提示啦。

下拉改写

在Compose中假如想完结下拉改写功用,能够直接运用accompanist-swiperefresh库,依靠如下

implementation("com.google.accompanist:accompanist-swiperefresh:$accompanist_version")

Compose中结合Paging实现上拉丝滑加载和下拉刷新

详细运用如上面代码,图片要点标红了四处当地,一个一个的解说下:

  • refresh表明当时下拉改写状况,默以为false,它是一个State状况哦
  • pullRefreshState目标是用于管理改写状况和改写之后详细的动作,这儿直接调用articleList.refresh()就能够触发Paging进行数据的改写动作
  • Modifier.pullRefersh()表明在哪个可组项上能够触发下拉改写动作
  • PullRefreshIndicator是一个改写指示器,也便是咱们一般看到的转圈圈的动画

经过上述代码咱们就完结了在Paging中下拉改写数据的作用咯,下面是详细的作用

Compose中结合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,假如觉得本文对你有所协助,帮忙点个赞或许保藏,谢谢~