【问题标题】:Is there a way to implement a cursor based pagination with Paging Library 3.0 in Android?有没有办法在 Android 中使用 Paging Library 3.0 实现基于光标的分页?
【发布时间】:2021-04-07 03:16:37
【问题描述】:

我正在使用一个 rest 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"
    }
}

【问题讨论】:

    标签: android kotlin android-recyclerview android-jetpack android-paging-library


    【解决方案1】:

    感谢@Đặng Anh Hào,我能够走上正轨。由于我的光标是 String 而不是 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&lt;Order, OrderAdapter.ViewHolder&gt;(OrdersDiffer)

    如需更详细的操作教程,I read this codelab

    【讨论】:

      【解决方案2】:

      在 kotlin 中,这里是示例。

      在活动中或某处:

      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)
          }
      }
      

      }

      【讨论】:

      • 这种方法适合基于光标的分页吗? API 给我一个带有分页对象的响应,该对象具有要为下一页或上一页调用的前后 URL。它也适用吗?
      • 请给我示例 api
      • 查看ExamplePagingSource类:接收数据后,需要修改请求调用下一个api。并根据您的上下文更改条件检查 prevKey、nextKey
      • 我已经用具有分页的 api 响应更新了问题,也许这让您更清楚地了解我想要实现的目标
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-30
      • 2018-02-06
      • 2021-11-16
      • 2021-04-09
      • 2018-11-30
      • 2020-11-18
      • 1970-01-01
      相关资源
      最近更新 更多