18

我目前正在考虑将分页架构库(2.1.0-beta01撰写本文时的版本)合并到我的应用程序中。一个组件是允许用户从中删除单个项目的列表。此列表仅限网络,使用 Room 进行本地缓存没有意义。

PagedList是不可变的,不支持修改。我已经读过,拥有一份列表副本,然后修改并作为新列表返回是要走的路。文档声明相同:

如果您有更精细的更新信号,例如网络 API 发出对列表中单个项目的更新信号,建议将数据从网络加载到内存中。然后通过包装内存快照的 DataSource 将该数据呈现给 PagedList。每次内存中的副本更改时,都会使先前的 DataSource 无效,并且可以创建一个包装快照新状态的新数据源。

我目前有基本的推荐实现来显示一个简单的列表。我的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 并且没有使整个数据集失效)的具体实现是什么样的?

4

2 回答 2

7

如果你想修改你的列表而不一直到数据层,你需要submitList在你的适配器中覆盖,然后在你的PagedList对象上设置一个回调。每当PagedList发生更改时,您都可以将这些更改复制到本地数据集。不建议这样做,但这是一个非常简单的 hack 开始工作。

这是一个例子:

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)
    }
}
于 2019-07-22T17:34:21.470 回答
4

你是对的,因为 aDataSource是用来保存不可变数据的。我相信这是因为 Room and Paging Library 正试图做出更有主见的设计决策并提倡不可变数据。

这就是为什么在官方文档中,他们有一个用于更新或变异数据集的部分应该在发生此类更改时使数据源无效。

更新分页数据:如果您有更精细的更新信号,例如网络 API 发出更新列表中单个项目的信号,建议将数据从网络加载到内存中。然后通过包装内存快照的 DataSource 将该数据呈现给 PagedList。每次内存中的副本更改时,都会使先前的 DataSource 无效,并且可以创建一个包装快照新状态的新数据源。

来源:https ://developer.android.com/reference/android/arch/paging/DataSource


考虑到这一点,我相信可以通过几个步骤来解决您描述的问题。

这可能不是最干净的方法,因为它涉及 2 个步骤。

您可以获取 PagedList 所持有的快照的引用,它是一个 type MutableList。然后,您可以只删除或更新该快照中的项目,而不会使数据源失效。

然后第二步是调用类似的东西notifyItemRemoved(index)or 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 来提供多个版本的网络分页列表。如果需要重新加载所有内容(例如响应滑动刷新之类的操作)以获取新版本的数据,您可以连接一个显式刷新信号以在当前数据源上调用 invalidate()。

来源:https ://developer.android.com/reference/android/arch/paging/DataSource

但是,我还没有为第二种方法找到一个好的解决方案。

于 2018-11-22T16:26:14.527 回答