我目前正在考虑将Paging Architecture库(在撰写本文时的版本2.1.0-beta01
)整合到我的应用中。一个组件是一个列表,该列表允许用户从中删除单个项目。此列表仅用于网络,并且使用Room缓存本地语言是没有道理的。
PagedList
是不可变的,不支持修改。我已经读过,有一份清单的副本已被修改并以新清单的形式返回,这是要走的路。该文档说明了相同的内容:
如果您有更细致的更新信号,例如网络API发出对列表中单个项目的更新信号,则建议将数据从网络加载到内存中。然后通过包装内存中快照的数据源将数据提供给PagedList。每次更改内存中的副本时,都将使先前的数据源无效,并且可以创建一个新的包装快照的新状态。
我目前有基本的推荐实现,以显示一个简单列表。我的DataSource
看起来像这样:
class MyDataSource<SomeItem> : PageKeyedDataSource<Int, SomeItem>() {
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, SomeItem>) {
// Simple load from API and notification of `callback`.
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
// Simple load from API and notification of `callback`.
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) {
// Simple load from API and notification of `callback`.
}
}
文档中引用的内存高速缓存(没有Room且不会使整个数据集无效)的具体实现是什么样的?
答案 0 :(得分:3)
您是正确的,因为DataSource
是用来保存不可变数据的。
我相信这是因为Room and Paging Library正在尝试做出更多有见地的设计决策,并倡导不可变数据。
这就是为什么在官方文档中,有一个部分用于更新或更改数据集,当发生此类更改时应该使数据源无效。
更新分页数据:如果您有更细致的更新信号(例如,网络API表示对列表中的单个项目进行了更新),建议将数据从网络加载到内存中。然后通过包装内存中快照的数据源将数据提供给PagedList。每次更改内存中的副本时,都将使先前的数据源无效,并且可以创建一个新的包装快照的新状态。
来源:https://developer.android.com/reference/android/arch/paging/DataSource
考虑到这一点,我相信可以通过几个步骤解决您所描述的问题。
这可能不是最干净的方法,因为它涉及两个步骤。
您可以获取PagedList所保存的快照的引用,该快照是MutableList
类型。然后,您可以删除或更新该快照中的项目,而无需使数据源无效。
然后第二步是调用notifyItemRemoved(index)
或notifyItemChanged(index)
之类的东西。
由于您不能强制DataSource将更改通知给观察者,因此必须手动执行。
pagedList.snapshot().remove(index) // Removes item from the pagedList
adapter.notifyItemRemoved(index) // Triggers recyclerview to redraw/rebind to account for the deleted item.
在您的DataSource.Factory
中可能找到了更好的解决方案。
根据官方文档,一旦数据更新,您的DataSource.Factory
应该会发出新的PagedList
。
更新分页数据:要分页确实提供更新的源中的数据,您可以创建一个DataSource.Factory,当对数据集进行更新时,其中创建的每个DataSource都会失效。当前快照无效。例如,在从数据库分页查询时,要查询的表将插入或删除项目。您还可以使用DataSource.Factory提供网络页面列表的多个版本。如果需要重新加载所有内容(例如,响应诸如滑动到刷新的操作)以获取新版本的数据,则可以连接显式刷新信号以在当前DataSource上调用invalidate()。
来源:https://developer.android.com/reference/android/arch/paging/DataSource
但是,对于第二种方法,我还没有找到好的解决方案。
答案 1 :(得分:0)
如果要修改列表而不完全进入数据层,则需要在适配器中覆盖submitList
,然后在PagedList
对象上设置回调。每当PagedList
发生更改时,您就可以将这些更改复制到本地数据集中。不建议这样做,但这只是一个最小的技巧。
这是一个例子:
class MyListAdapter : PagedListAdapter<MyDataItem, MyViewHolder>(MyDiffCallback()) {
/**
* This data set is a bit of a hack -- we are copying everything the PagedList loads into our
* own list. That way we can modify it. The docs say you should go all the way down to the
* data source, modify it there, and then bubble back up, but I don't think that will actually
* work for us when the changes are coming from the UI itself.
*/
private val dataSet = arrayListOf<MyDataItem>()
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
//Forces the next page to load when we reach the bottom of the list
getItem(position)
dataSet.getOrNull(position)?.let {
holder.populateFrom(it)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = parent.inflate(R.layout.my_view_holder)
return MyViewHolder(view)
}
class MyDiffCallback : DiffUtil.ItemCallback<MyDataItem>() {
override fun areItemsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
oldItem == newItem
}
override fun submitList(pagedList: PagedList<MyDataItem>?) {
pagedList?.addWeakCallback(listOf(), object : PagedList.Callback() {
override fun onChanged(position: Int, count: Int) {
dataSet.clear()
dataSet.addAll(pagedList)
}
override fun onInserted(position: Int, count: Int) {
dataSet.clear()
dataSet.addAll(pagedList)
}
override fun onRemoved(position: Int, count: Int) {
dataSet.clear()
dataSet.addAll(pagedList)
}
})
super.submitList(pagedList)
}
}