Jetpack Compose 无限加载列表(滚到底部自动加载更多)

Posted fundroid_方卓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose 无限加载列表(滚到底部自动加载更多)相关的知识,希望对你有一定的参考价值。

android 中使用 ListView 或者 RecycleView 经常有滚动到底部自动 LoadMore 的需求,那么在 Compose 中该如何实现呢?

两种方法可供选择:

  1. 基于 paging-compose
  2. 自定义实现

方法一: paging-compose

Jetpack 的 Paging 组件提供了对 Compose 的支持

dependencies 
    ...
    // Paging Compose    
    implementation "androidx.paging:paging-compose:$latest_version"

Paging 的无限加载列表需要依靠 Paging 的 DataSource,我们创建一个 DataSource ,并在 ViewModel 中加载

class MyDataSource(
    private val repo: MyRepository
) : PagingSource<Int, MyData>() 

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MyData> 
        return try 
            val nextPage = params.key ?: 1
            val response = repo.fetchData(nextPage)

            LoadResult.Page(
                data = response.results,
                prevKey = if (nextPage == 1) null else nextPage - 1,
                nextKey = repo.page.plus(1)
            )
         catch (e: Exception) 
            LoadResult.Error(e)
        
    


class MainViewModel(
    repo: MyRepository
) : ViewModel() 

    val pagingData: Flow<PagingData<MyData>> = Pager(PagingConfig(pageSize = 20)) 
        MyDataSource(repo)
    .flow


接下来在 Compose 使用 LazyColumn 或者 LazyRow 显示 Paging 的数据

@Composable
fun MyList(pagingData: Flow<PagingData<MyData>>) 
    val listItems: LazyPagingItems<MyData> = pagingData.collectAsLazyPagingItems()

    LazyColumn 
        items(listItems)  
            ItemCard(data = it)
        
    

MyList 从 ViewModel 获取 Flow<PagingData<MyData>> 并通过 collectAsLazyPagingItems 转换成 Compose 的 State ,最终传递给 LazyColumn 内的 items 中进行展示。

注意这里的 items(...) 是 paging-compose 中为 LazyListScope 定义的扩展方法,而非通常使用的 LazyListScope#items

public fun <T : Any> LazyListScope.items(
    items: LazyPagingItems<T>,
    key: ((item: T) -> Any)? = null,
    itemContent: @Composable LazyItemScope.(value: T?) -> Unit
) 
	...

它接受的参数类型是 LazyPagingItems<T>, LazyPagingItems 在 get 时会判断是否滑动到底部并通过 Paging 请求新的数据,以此实现了自动加载。

方法二:自定义实现

如果你不想使用 Paging 的 DataSource,也可以自定义一个无限加载列表的 Composable

@Composable
fun list() 
    val listItems = viewModel.data.observeAsState()
    LazyColumn 
        listItems.value.forEach  item ->
            item  ItemCard(item)     
        
        item  
        	LaunchedEffect(Unit) 
        		viewModel.loadMore()
        	
        
    

当加载到最后一个 item 时,通过 LaunchedEffect 向 viewModel 请求新的数据。
你也可以是用下面的方法,在抵达最后一个 item 之前,提前 loadmore,

@Composable
fun list() 
    val listItems = viewModel.data.observeAsState()
    LazyColumn 
    	itemsIndexed(listItmes)  index, item ->
    		itemCard(item)
    		LaunchedEffect(listItems.size) 
    			if (listItems.size - index < 2) 
    				viewModel.loadMore()
    			
    		
		
    

如上,使用 itemsIndexed() 可以在展示 item 的同时获取当前 index,每次展示 item 时,都判断一下是否达到 loadMore 条件,比如代码中是当距离抵达当前列表尾部还有 2 个以内 item 。
注意 LaunchedEffect 可能会随着每个 item 重组而执行,为 LaunchedEffect 增加参数 listItems.size 是为了确保对其在 item 上屏时只走一次。

添加 LoadingIndicator

如果想在 loadMore 时显示一个 LoadingIndicator, 可以实现代码如下

@Composable
fun list() 
    val listItems = viewModel.data.observeAsState()
    val isLast = viewModel.isLast.observeAsState()
    
    LazyColumn 
        listItems.value.forEach  item ->
            item  ItemCard(item)     
        
		if (isLast.value.not()) 
		    item  
        		LoadingIndicator()
        		LaunchedEffect(Unit) 
        			viewModel.loadMore()
        		
        	
		
    

在展示最后一个 item 时,显示 LoadingIndicator() ,同时 loadMore(), 当没有数据可以加载时,不能再显示 LoadingIndicator,所以我们必须将 isLast 作为一个状态记录到 ViewModel 中,当然,你也可以将 viewModel.data 和 viewModel.isLast 等所有状态合并为一个 UiState 进行订阅。

以上是关于Jetpack Compose 无限加载列表(滚到底部自动加载更多)的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose 实现波浪加载效果

如何从 Jetpack compose 中的 drawable 加载图像?

Jetpack Compose:列表更改时更新可组合

Jetpack Compose中的列表

Jetpack Compose 列表的展开与收起颜色动画效果

jetpack compose 上的垂直列表中的水平列表不顺畅