Сохранять предыдущий обновленный элемент в PagedListAdapter после того, как список был обновлен из источника данных - PullRequest
0 голосов
/ 14 июля 2020

У меня есть RecyclerView, который использует PagedListAdapter, который извлекает данные из ROOM и сетевого API. Он использует BoundaryCallback для выполнения запроса к API, возвращенные данные которого вставляются в базу данных (ROOM)

У меня есть элемент списка с кнопкой увеличения и уменьшения ... введите описание изображения здесь

Текущий список можно фильтровать, например, я могу отфильтровать список продуктов по нескольким категориям

Проблема

Если я увеличиваю продукт элемент quantity с помощью кнопки увеличения, например, до 12, а затем я пытаюсь отфильтровать список, добавляя дополнительные категории, текущий список не обновляется sh, что нормально, потому что DiffUtil.ItemCallback подтверждает, что элементы совпадают, но как только я пытаюсь увеличить количество того же товара после фильтрации по другим категориям, оно снова начинается с нуля ....

Обратите внимание, что количество - это не столбец в комнате, это игнорируемая переменная.

Итак, я не совсем уверен, в чем проблема на самом деле: ниже приведен код, который выполняет увеличение и уменьшение.

override fun onIncrementQuantity(position: Int, item: ProductEntity) {
    item.quantity = item.quantity + 1
    selectedProducts[item.item.id!!] = item
    productAdapter?.notifyItemChanged(position, item)
}
override fun onDecrementQuantity(position: Int, item: ProductEntity) {
    item.quantity = if (item.quantity == 0) 0 else item.quantity - 1
    if (item.quantity == 0) {
        selectedProducts.remove(item.item.id)
    } else {
        selectedProducts[item.item.id!!] = item
    }
    productAdapter?.notifyItemChanged(position, item)
}

1 Ответ

0 голосов
/ 14 июля 2020

Итак, после нескольких часов отладки, вот что я узнал.

  • Список изначально загружен с данными
  • обновления выполняются для текущих данных, например, приращение item.quantity = 2
  • После получения новых данных (через поиск или фильтры) onBindViewHolder НЕ вызывается, если itemsAretheSame или ContentsAreThemSame , поэтому нет необходимости обновлять представление. На данный момент все в порядке.

Однако кажется, что currentList был обновлен с учетом вновь полученных данных без обязательного обновления представления ... Теперь, когда вызывается notifyItemChanged, запускается onBindViewHolder :

Вот как мой onBindViewHolder равен

override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) {
    getItem(position)?.let { holder.bind(it) }
}

getItem(position) дает вам элемент из текущего списка, который был обновлен, и количество возвращается к значению по умолчанию 0.

Решение

Переопределите приведенную ниже функцию RecyclerView.Adapter

override fun onBindViewHolder(
    holder: ViewHolder<ProductEntity>, 
    pos: Int, 
    payloads: MutableList<Any>
) { 
    if(payloads.isEmpty()) return super.onBindViewHolder(holder, pos, payloads)
    val product = getItem(pos)?: return
    payloads.forEach {
       val oldProduct = it as ProductEntity
       if(product.item.id == oldProduct.item.id){
          product.quauntity = oldProduct.quantity
       }
    }
}

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

По документации

     * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
     * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
     * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
     * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
     * Adapter should not assume that the payload passed in notify methods will be received by
     * onBindViewHolder().  For example when the view is not attached to the screen, the
     * payload in notifyItemChange() will be simply dropped.
...