Изменение PagedList в библиотеке Android Paging Architecture - PullRequest
0 голосов
/ 11 ноября 2018

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

Ответы [ 2 ]

0 голосов
/ 22 июля 2019

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

Вы правы в том, что 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

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

...