有没有办法在 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
我正在休息 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