You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在现有架构中使用Paging Library 3实现Jetpack Compose分页功能

Jetpack Compose 集成 Paging 3 实现分页步骤

1. 添加依赖

首先在模块级build.gradle中添加Paging3相关依赖:

dependencies {
    // Paging 3 核心依赖
    implementation "androidx.paging:paging-runtime-ktx:3.2.1"
    // Compose 适配依赖
    implementation "androidx.paging:paging-compose:3.2.1"
}

2. 改造网络请求接口

首先需要你的后端接口支持分页传参,通常需要传入页码page、每页条数pageSize,对应改造Services接口:

// 示例改造搜索接口,根据你后端实际参数调整
@GET("category/busca/{title}")
suspend fun getSearch(
    @Path("title") title: String,
    @Query("page") page: Int,
    @Query("pageSize") pageSize: Int
): List<ShoppingCart>

3. 实现PagingSource

自定义PagingSource处理分页加载逻辑:

class SearchPagingSource(
    private val repository: SearchRepository,
    private val searchKeyword: String
) : PagingSource<Int, ShoppingCart>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ShoppingCart> {
        return try {
            // 当前页码,首次加载为null,默认取第1页
            val currentPage = params.key ?: 1
            // 请求数据
            val data = repository.getSearch(searchKeyword, currentPage, params.loadSize)
            // 计算前后页页码,逻辑根据你后端返回规则调整
            val prevPage = if (currentPage > 1) currentPage -1 else null
            val nextPage = if (data.isNotEmpty()) currentPage +1 else null
            LoadResult.Page(
                data = data,
                prevKey = prevPage,
                nextKey = nextPage
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, ShoppingCart>): Int? {
        // 刷新时用的key,可直接返回null或者取最近访问的页码
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }
}

4. 改造Repository层

修改SearchRepository,新增返回PagingData的方法:

class SearchRepository {
    private val retrofit = Retrofit()
    private val search = retrofit.frutasServices

    // 原有方法保留,新增分页请求方法
    suspend fun getSearch(keyword: String, page: Int, pageSize: Int) =
        search.getSearch(keyword, page, pageSize)

    // 提供分页数据流
    fun getSearchPagingFlow(keyword: String): Flow<PagingData<ShoppingCart>> {
        return Pager(
            config = PagingConfig(
                pageSize = 10, // 每页加载条数,按需调整
                enablePlaceholders = false,
                initialLoadSize = 10 // 首次加载条数
            ),
            pagingSourceFactory = { SearchPagingSource(this, keyword) }
        ).flow
    }
}

5. 改造ViewModel层

修改SearchViewModel,直接暴露PagingData流,无需自己维护列表状态:

class SearchViewModel: ViewModel() {
    private val searchRepository = SearchRepository()

    // 搜索结果分页流
    val searchPagingFlow: MutableStateFlow<Flow<PagingData<ShoppingCart>>?> = MutableStateFlow(null)

    fun buscar(title: String) {
        searchPagingFlow.value = searchRepository.getSearchPagingFlow(title)
            .cachedIn(viewModelScope) // 绑定ViewModel生命周期,页面重建不用重新请求
    }
}

6. Compose UI层渲染分页列表

在Composable中收集PagingData,用LazyPagingItems渲染列表,同时可以处理加载中、加载失败、空列表等状态:

@Composable
fun SearchListScreen(viewModel: SearchViewModel = viewModel()) {
    // 触发搜索
    LaunchedEffect(Unit) {
        viewModel.buscar("你的搜索关键词")
    }

    val pagingFlow = viewModel.searchPagingFlow.collectAsStateWithLifecycle().value
    pagingFlow?.let { flow ->
        val lazyPagingItems = flow.collectAsLazyPagingItems()

        LazyColumn {
            items(lazyPagingItems) { item ->
                // 你的列表Item布局
                item?.let { ShoppingCartItem(it) }
            }

            // 处理列表加载状态
            lazyPagingItems.apply {
                when {
                    loadState.refresh is LoadState.Loading -> {
                        // 首次加载loading
                        item { LoadingItem() }
                    }
                    loadState.refresh is LoadState.Error -> {
                        // 首次加载失败
                        val error = loadState.refresh as LoadState.Error
                        item { ErrorItem(error.error.message ?: "加载失败") { retry() } }
                    }
                    loadState.append is LoadState.Loading -> {
                        // 加载下一页loading
                        item { AppendLoadingItem() }
                    }
                    loadState.append is LoadState.Error -> {
                        // 加载下一页失败
                        val error = loadState.append as LoadState.Error
                        item { AppendErrorItem(error.error.message ?: "加载更多失败") { retry() } }
                    }
                }
            }
        }
    }
}

注意:如果你的后端分页参数规则、返回结构和示例不同,需要对应调整PagingSource中的页码计算、下一页判断逻辑。

内容的提问来源于stack exchange,提问作者Rafael Souza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 11:21:04