Изменение PagedList в библиотеке Android Paging Architecture

В настоящее время я смотрю на включение библиотеки Paging Architecture (версия 2.1.0-beta01 на момент написания) в мое приложение. Одним из компонентов является список, который позволяет пользователю удалять отдельные элементы из него. Этот список только для сети и кэширование локально с Room не имеет смысла.

PagedList является неизменным и не поддерживает модификацию. Я читал, что наличие копии списка, который затем модифицируется и возвращается как новый, - это путь. В документации говорится то же самое:

Если у вас есть более детальные сигналы обновления, такие как сетевой API, сигнализирующий об обновлении одного элемента в списке, рекомендуется загружать данные из сети в память. Затем представьте эти данные в 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 и без аннулирования всего набора данных), как указано в документации?

1 ответ

Если вы хотите изменить свой список, не доходя до уровня данных, вам нужно будет переопределить 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)
    }
}

Вы правы в этом DataSource предназначен для хранения неизменных данных. Я полагаю, что это потому, что Room and Paging Library стараются принимать более взвешенные дизайнерские решения и отстаивают неизменные данные.

Вот почему в официальных документах есть раздел для обновления или изменения вашего набора данных, который должен сделать недействительным источник данных, когда такое изменение произойдет.

Обновление выгружаемых данных: если у вас есть более детализированные сигналы обновления, такие как сетевой API, сигнализирующий об обновлении отдельного элемента в списке, рекомендуется загружать данные из сети в память. Затем представьте эти данные в PagedList через DataSource, который оборачивает снимок в памяти. Каждый раз, когда изменяется копия в памяти, аннулируется предыдущий источник данных, и может быть создан новый источник, содержащий новое состояние моментального снимка.

Источник: https://developer.android.com/reference/android/arch/paging/DataSource


Имея это в виду, я считаю, что можно решить проблему, которую вы описали, с помощью нескольких шагов.

Это может быть не самым чистым способом, так как включает 2 шага.

Вы можете получить ссылку на снимок, который содержит 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 для предоставления нескольких версий списков с разбивкой по сети. Если для получения новой версии данных требуется перезагрузка всего содержимого (например, в ответ на действие, например, свайп для обновления), вы можете подключить явный сигнал обновления для вызова invalidate() в текущем DataSource.

Источник: https://developer.android.com/reference/android/arch/paging/DataSource

Однако я не нашел хорошего решения для этого второго подхода.

Другие вопросы по тегам