分页库 3.0:如何将项目总数传递给列表标题?
Posted
技术标签:
【中文标题】分页库 3.0:如何将项目总数传递给列表标题?【英文标题】:Paging library 3.0 : How to pass total count of items to the list header? 【发布时间】:2021-01-06 17:07:20 【问题描述】:请帮帮我。 该应用程序仅用于接收来自https://trefle.io 的植物列表并将其显示在 RecyclerView 中。 我在这里使用的是 Paging library 3.0。任务:我想添加一个标题,其中将显示植物的总量。问题:我只是找不到将总项目的值传递给标题的方法。
Data model:
data class PlantsResponseObject(
@SerializedName("data")
val data: List<PlantModel>?,
@SerializedName("meta")
val meta: Meta?
)
data class Meta(
@SerializedName("total")
val total: Int? // 415648
)
data class PlantModel(
@SerializedName("author")
val author: String?,
@SerializedName("genus_id")
val genusId: Int?,
@SerializedName("id")
val id: Int?)
数据源类:
class PlantsDataSource(
private val plantsApi: PlantsAPI,
private var filters: String? = null,
private var isVegetable: Boolean? = false
) : RxPagingSource<Int, PlantView>()
override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, PlantView>>
val nextPageNumber = params.key ?: 1
return plantsApi.getPlants( //API call for plants
nextPageNumber, //different filters, does not matter
filters,
isVegetable)
.subscribeOn(Schedulers.io())
.map<LoadResult<Int, PlantView>>
val total = it.meta?.total ?: 0 // Here I have an access to the total count
//of items, but where to pass it?
LoadResult.Page(
data = it.data!! //Here I can pass only plant items data
.map PlantView.PlantItemView(it) ,
prevKey = null,
nextKey = nextPageNumber.plus(1)
)
.onErrorReturn
LoadResult.Error(it)
override fun invalidate()
super.invalidate()
LoadResult.Page 只接受植物本身的列表。并且 DataSource(Repo, ViewModel, Activity) 上面的所有类都无法访问响应对象。问题:如何将项目总数传递给列表标题? 我将不胜感激。
【问题讨论】:
【参考方案1】:第一次尝试使用 Paging 时面临同样的困境,尽管它出于分页的目的进行计数,但它没有提供获取计数的方法(即 Paging
库首先使用 @987654322 检查@在进行其余查询之前查看是否有比规定的PagingConfig
值更多或更少的项目,它可以完美地返回它找到的结果总数。
目前实现此目的的唯一方法是并行运行两个查询:一个用于您的项目(正如您已经拥有的那样),另一个只是使用与前一个相同的查询参数计算找到多少结果,但仅限COUNT(*)
。
没有必要将后者返回为PagingDataSource<LivedData<Integer>>
,因为它会不必要地添加大量样板文件。只需将其作为正常的LivedData<Integer>
返回,以便在列表结果更改时始终更新自身,否则它可能会遇到列表大小更改的问题,并且如果您返回纯Integer
.
在您设置好它们之后,然后使用 ConcatAdapter
将它们添加到您的 RecyclerView
适配器,前面提到的适配器的顺序与您希望它们显示在列表中的顺序相同。
例如:如果您希望计数显示在列表的开头/顶部,则首先使用计数适配器设置 ConcatAdapter
,然后设置列表项适配器。
【讨论】:
【参考方案2】:一种方法是使用 MutableLiveData 然后观察它。例如
val countPlants = MutableLiveData<Int>(0)
override fun loadSingle(.....
countPlants.postValue(it.meta?.total ?: 0)
然后是你的 recyclerview 所在的地方。
pagingDataSource.countPlants.observe(viewLifecycleOwner) count ->
//update your view with the count value
【讨论】:
【参考方案3】:Paging 中的 withHeader 函数只返回一个 ConcatAdapter
给定一个 LoadStateHeader
,它有一些代码可以根据适配器的 LoadState
进行侦听和更新。
您应该能够通过实现自己的ItemCountAdapter
来做一些非常相似的事情,只是它不是监听LoadState
的变化,而是监听adapter.itemCount
。您需要构建一个流/侦听器来决定何时发送更新,但您可以简单地将 loadState 更改映射到 itemCount。
LoadStateAdapter
代码见这里,基本可以复制,把loadState
改成itemCount
:https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:paging/runtime/src/main/java/androidx/paging/LoadStateAdapter.kt?q=loadstateadapter
例如,
abstract class ItemCountAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>()
var itemCount: Int = 0
set(itemCount ...
open fun displayItemCountAsItem(itemCount: Int): Boolean
return true
...
然后要实际创建ConcatAdapter
,您需要类似于:https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt;l=236?q=withLoadStateHeader&sq=
fun PagingDataAdapter.withItemCountHeader(itemCountAdapter): ConcatAdapter
addLoadStateListener
itemCountAdapter.itemCount = itemCount
return ConcatAdapter(itemCountAdapter, this)
【讨论】:
adapter.itemCount 与我的问题无关。植物总数约为 300,000,我想在加载 30 株植物的第一部分后立即在适配器中显示此值。我在服务器响应中从 Meta 对象中提取植物总量。 我明白了,如果我误读了你的问题,我很抱歉。无论您如何填充/查询植物总数,总体思路都应该是相似的。即,ConcatAdapter 使用 PagingDataAdapter + 与 LoadStateAdapter 非常相似的东西生成,但经过定制以显示初始加载何时完成且不为空,而不是简单地监听 Prepend / append 加载状态更新。如果有什么我可以澄清的,请告诉我。【参考方案4】:您可以将PagingData
类型更改为Pair<PlantView,Int>
(或任何其他结构)以添加您需要的任何信息。
然后,您将能够发送总计页面,这些页面执行类似的操作:
LoadResult.Page(
data = it.data.map Pair(PlantView.PlantItemView(it), total) ,
prevKey = null,
nextKey = nextPageNumber.plus(1)
)
在您的ModelView
中执行任何操作,例如再次将其映射到PlantItemView
,但使用second
字段更新您的标题。
确实,它不是很优雅,因为您要在所有项目中发送它,但它比其他建议的解决方案要好。
【讨论】:
【参考方案5】:另一种解决方案,虽然也不是很优雅,但是将总量添加到您的数据模型 PlantView。
PlantView(…val totalAmount: Int…)
然后在您的视图模型中,您可以添加包含一项信息的标题。下面是摘自官方paging documenation的一点修改代码
pager.flow.map pagingData: PagingData<PlantView> ->
// Map outer stream, so you can perform transformations on
// each paging generation.
pagingData
.map plantView ->
// Convert items in stream to UiModel.PlantView.
UiModel.PlantView(plantView)
.insertSeparators<UiModel.PlantView, UiModel> before, after ->
when
//total amount is used from the next PlantView
before == null -> UiModel.SeparatorModel("HEADER", after?.totalAmount)
// Return null to avoid adding a separator between two items.
else -> null
一个缺点是总量在每个 PlantView 中,而且总是相同的,因此是多余的。
【讨论】:
【参考方案6】:目前,我发现这条评论很有用:https://issuetracker.google.com/issues/175338415#comment5
有人讨论向 Pager 提供元数据状态的方法
【讨论】:
以上是关于分页库 3.0:如何将项目总数传递给列表标题?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Google 的分页库更新 PagedList 中的单个项目
在使用 Paging library 3.0 时,我们如何将整个对象列表传递给 PagingDataAdapter?