在Android Paging Architecture库中修改PagedList

时间:2018-11-10 21:43:38

标签: android kotlin android-architecture-components android-paging

我目前正在考虑将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且不会使整个数据集无效)的具体实现是什么样的?

2 个答案:

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