Nano Hash - криптовалюты, майнинг, программирование

Изменение PagedList в библиотеке архитектуры подкачки Android

В настоящее время я собираюсь включить в свое приложение библиотеку 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`.
    }
}

Как будет выглядеть конкретная реализация кеша в памяти (без помещения и без признания недействительным всего набора данных), как указано в документации?


Ответы:


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)
    }
}
22.07.2019
  • Почему вы каждый раз очищаете набор данных? 29.10.2019
  • @masterwok Google очень странным образом построил библиотеку подкачки. submitList вызывается только один раз, и вся загрузка страницы выполняется внутри класса PagedList. Таким образом, экземпляр pagedList здесь всегда содержит последние обновления списка, и мы используем PagedList.Callback, чтобы знать, когда обновлять dataSet. Мы хотим, чтобы dataSet отражал pagedList, поэтому мы очищаем его и добавляем все из pagedList. 29.10.2019
  • как вы изменили значения в PagedList ‹MyDataItem›, который передается в submitList () в классе адаптера? Я загружаю данные из сети без базы данных. Мне нужно изменить свойство класса POJO, содержащегося в PagedList, на основе действия пользователя и обновить recyclerview. Итак, я хотел бы знать, как изменить pagedList и обновить пользовательский интерфейс. 25.02.2020
  • @ android.fryo вы захотите оставить submitList в покое и вместо этого изменить dataSet. submitList следует звонить только один раз. 27.02.2020
  • как мы можем изменить набор данных? PagedList неизменен. 27.02.2020
  • Измените свойство dataSet, как в примере выше. 02.03.2020
  • Спасибо чувак! спаси мой день. также нам нужно вернуть размер набора данных как счетчик списка: override fun getItemCount(): Int { return dataSet.size } 26.05.2020
  • @bonioctavianus, не могли бы вы поделиться своим кодом ... это довольно срочно 29.05.2020
  • любой пример в java? 15.08.2020
  • где держатель. populateFrom объявлен? 16.04.2021

  • 2

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

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

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

    22.11.2018
  • Спасибо за подробный ответ, но такой подход не работает. Возвращаемый снимок может иметь тип MutableList, но фактически не реализует интерфейс. Вызов, например, removeAt дает UnsupportedOperationException. Кроме того, в документации указано, что снимки не предназначены для отражения изменений и фактически также являются неизменяемыми. Так что спасибо за ответ, но голосование против, потому что это неверно и может ввести в заблуждение других людей. 23.11.2018
  • Ах я вижу. Тогда я могу думать только о втором варианте, упомянутом выше, и использовании DataSource.Factory. 24.11.2018
  • Да, похоже, это правильный путь. Я еще не нашел способа реализовать это и попытался получить помощь по этому вопросу. 25.11.2018
  • @rubengees Вы нашли решение? 31.05.2019
  • @LeoDroidcoder Нет, я еще не использую библиотеку подкачки в своем приложении (пока что-то подобное не станет возможным). 01.06.2019
  • Фактически вы можете изменять элементы внутри списка 21.09.2019
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..