有没有办法在 Android 中使用 Paging Library 3.0 实现基于游标的分页?

Is there a way to implement a cursor based pagination with Paging Library 3.0 in Android?

我正在休息 API,它使用基于游标的分页来显示一些结果。我想知道我是否可以使用 Paging Library 3.0 对其进行分页。我一直在浏览一些媒体和文档,但似乎找不到实现它的方法。如果你们中的任何人找到任何解决方案,我将很高兴听到它!

api 响应分页如下所示:

"paging": {
    "previous": false,
    "next": "https://api.acelerala.com/v1/orders/?store_id=4&after=xyz",
    "cursors": {
        "before": false,
        "after": "xyz"
    }
}

在 kotlin 中,这里是示例。

在Activity或某处:

viewModel.triggerGetMoreData("data").collectLatest {
                mAdapter.submitData(it)
            }

在视图模型中:

fun triggerGetMoreData(data: String): Flow<PagingData<SampleData>> {
    val request = ExampleRequest(data)
    return exampleRepository.getMoreData(request).cachedIn(viewModelScope)
}

在存储库中:

fun getMoreData(request: ExampleRequest): Flow<PagingData<ExampleData>> {
    return Pager(
        config = PagingConfig(
            pageSize = 30,
            enablePlaceholders = false
        ),
        pagingSourceFactory = { ExamplePagingSource(service, request) }
    ).flow
}

class ExamplePagingSource (
private val service: ExampleService,
private val request: ExampleRequest): PagingSource<Int, ExampleData>() {

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ExampleData> {
    return try {
        val pageIndex = params.key ?: 0
        val request = request.copy(index = (request.pageNum.toInt() * pageIndex).toString())
        when (val result = service.getMoreData(request)) { // call api
            is NetworkResponse.Success -> {
                val listData = result.body.items?.toData()?: listOf()
                LoadResult.Page(
                    data = listData,
                    prevKey = if (pageIndex == 0) null else pageIndex - 1,
                    nextKey = if (listData.isEmpty()) null else pageIndex + 1
                )
            }
            else -> LoadResult.Error(result.toError())
        }
    } catch (e: Exception) {
        e.printStackTrace()
        LoadResult.Error(e)
    }
}

}

多亏了@Đặng Anh Hào,我才能走上正轨。由于我的光标是字符串而不是 Int,因此 Paging Source 加载函数如下所示:

override suspend fun load(params: LoadParams<String>): LoadResult<String, Order> {
    return try{
        val response = service.getOrders(query,params.key?:"",10)
        val nextKey = if(response.paging?.cursors?.after=="false") null else response.paging?.cursors?.after
        val prevKey = if(response.paging?.cursors?.before=="false") null else response.paging?.cursors?.before
        LoadResult.Page(response.data?.toOrderList()?:emptyList(),prevKey,nextKey)
    }catch (exception: IOException) {
        LoadResult.Error(exception)
    } catch (exception: retrofit2.HttpException) {
        LoadResult.Error(exception)
    }
}

onrefreshkey 如下所示:

override fun getRefreshKey(state: PagingState<String, Order>): String? {
    return state.anchorPosition?.let {
        state.closestItemToPosition(it)?.orderId
    }
}

存储库方法如下所示:

fun getOrdersPaginated(storeId: String): Flow<PagingData<Order>> {
    return Pager(
        config = PagingConfig(enablePlaceholders = false,pageSize = 10),
        pagingSourceFactory = {PagingSource(apiService,storeId)}
    ).flow

}

View Model方法是这样的:

private val _pagedOrders = MutableLiveData<PagingData<Order>>()
val orders get() = _pagedOrders

private var currentQueryValue: String? = null
private var currentSearchResult: Flow<PagingData<Order>>? = null

fun getOrdersPaginated(storeId: String) {
    viewModelScope.launch {
        currentQueryValue = storeId
        val newResult: Flow<PagingData<Order>> = repository.getOrdersPaginated(storeId)
            .cachedIn(viewModelScope)
        currentSearchResult = newResult
        currentSearchResult!!.collect {
            _pagedOrders.value = it
        }
    }
}

activity这样调用分页:

private var searchJob: Job? = null

private fun getOrders() {
    viewModel.getOrdersPaginated(storeId)
}

private fun listenForChanges() {
    viewModel.orders.observe(this, {
        searchJob?.cancel()
        searchJob = lifecycleScope.launch {
            ordersAdapter.submitData(it)
        }
    })
}

最后,适配器与 ListAdapter 相同,唯一不同的是它现在扩展了 PagingDataAdapter<Order, OrderAdapter.ViewHolder>(OrdersDiffer)

有关如何操作的更详细教程,I read this codelab