PagedListAdapter.submitList() странное поведение при обновлении существующих элементов

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

Когда элемент добавлен или удален, последний набор данных извлекается и передается в метод submitList, тогда все изменения видны и работают хорошо.

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

Когда я отлаживаю DIFF_CALLBACK и поймал мой предмет в areItemsTheSame, newHistory а также oldHistory значения одинаковы! (Как!)

Там может быть любая ошибка в submitList метод?

  • Комната v.: 2.1.0-alpha02
  • Paging v.: 2.1.0-beta01

После инициализации observe извлекает список из комнаты и передает mHistoryAdapter.submitList(it), Затем, если я обновляю элемент, наблюдение снова срабатывает (и я вижу обновленное значение в параметре it) и переходит к submitList,

К сожалению, адаптер не изменится...

    mResolvedAddressViewModel = ViewModelProviders.of(this).get(ResolvedAddressViewModel::class.java)
    mResolvedAddressViewModel.getAddresses(false).observe(this, Observer {
        mHistoryAdapter.submitList(it)
    })

Все части

модель

@Parcelize
@Entity
data class ResolvedAddress(
    @PrimaryKey var id: String = UUID.randomUUID().toString(),
    var requestedLat: Double = 0.0,
    var requestedLon: Double = 0.0,
    var requestedAddress: String = "",
    var lat: Double,
    var lon: Double,
    var address: String,
    var country: String,
    var countryCode: String,
    var city: String,
    var alias: String? = null,
    var favorite: Boolean = false,
    var provider: String? = null,
    var lastUseDate: Long = 0L) : Parcelable

адаптер

class HistoryAdapter(var context: Context)
: PagedListAdapter<ResolvedAddress, HistoryItemHolder>(DIFF_CALLBACK) {

    companion object {
        private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ResolvedAddress>() {
            override fun areItemsTheSame(
                oldHistory: ResolvedAddress, newHistory: ResolvedAddress): Boolean {
                return oldHistory.id == newHistory.id
            }

            override fun areContentsTheSame(
                oldHistory: ResolvedAddress, newHistory: ResolvedAddress): Boolean {
                return oldHistory == newHistory
            }
        }
    }
}

Фрагмент

class HistoryFragment : Fragment() {
    private lateinit var mHistoryAdapter: HistoryAdapter
    private lateinit var mResolvedAddressViewModel: ResolvedAddressViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 
        savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_history, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recyclerViewHistory.setHasFixedSize(true)
        recyclerViewHistory.layoutManager = LinearLayoutManager(activity)
        recyclerViewHistory.itemAnimator = DefaultItemAnimator()

        mHistoryAdapter = HistoryAdapter(context!!)
        recyclerViewHistory.adapter = mHistoryAdapter

        mResolvedAddressViewModel = ViewModelProviders.of(this)
        .get(ResolvedAddressViewModel::class.java)

        mResolvedAddressViewModel.getAddresses(false).observe(this, Observer {
            mHistoryAdapter.submitList(it)
        })
    }
}

1 ответ

В вопросе отсутствует пара вещей, которые могли бы помочь дать более подробный ответ.

Ex. Что делает твой RecyclerView.Adapter выглядит как? Это распространяется PagedListAdapter?

Как выглядит ваш модельный класс? Это класс данных Kotlin?


Чтобы дать ответ, давайте предположим, что эти неизвестные - это то, что мы ожидаем.

Если я понимаю вопрос, кажется, что вы просто updating элемент, а не удаление или добавление каких-либо элементов. Следовательно DiffUtil.ItemCallback"s areItemsTheSame всегда будет возвращать true, потому что старый список и новый список не были изменены с точки зрения их размера. Это означает, что если вы обновили элемент, вы, вероятно, обновили его содержимое и не удалили его из списка.

Следовательно, areItemsTheSame вернет true, потому что их идентификаторы все те же.

Скорее всего, второй метод, areContentsTheSame вернет false, так как вы обновили содержимое элемента.

Если ваш модельный класс, ResolvedAddress, это класс данных Kotlin, то метод areContentsTheSame должен возвращать false при сравнении элемента, который был обновлен из старого списка и нового списка. Это должно вызвать onBindViewHolder метод в вашем адаптере на этом этапе, чтобы вы связали этот элемент с обновленными данными.

Если эта модель не Kotlin data classчем вы должны убедиться, что класс реализует compareTo метод. Если нет, вы сравниваете адрес памяти объекта с фактическим содержимым объекта. Если это так, метод areContentsTheSame всегда будет возвращать true, так как адрес памяти объекта не изменился.

Это несколько советов по отладке, так как трудно дать более четкий ответ без дополнительных знаний о том, как реализован код.

У меня была похожая проблема, но мне удалось ее исправить, обновив существующий элемент новым объектом, а не напрямую обновляя существующий элемент, как предлагается в следующем ответе:

/questions/29225396/pagedlistadapter-ne-obnovlyaet-spisok-esli-menyaetsya-tolko-soderzhimoe-elementa/29225421#29225421

Проблема в том, как submitListпроцессы меняются. Если вы передаете ссылку на тот же список, он не будет отображать обновления, поскольку определит, что это тот же источник данных. В Kotlin, если вы хотите обновить список источников и передать его обратно вsubmitList, вы можете сделать это следующим образом:

submitList(originalList.toList().toMutableList().let {
     it[index] = it[index].copy(property = newvalue) // To update a property on an item
     it.add(newItem) // To add a new item
     it.removeAt[index] // To remove an item
     // and so on....
     it
})

Объект в наборе данных адаптера обновляется только тогда, когда метод areItemsTheSame или areContentsTheSame возвращает false.

Ваш метод areContentsTheSame должен позаботиться обо всех случаях, когда переменные в объекте могут обновляться во время выполнения, и должен возвращать значение false, если какая-либо переменная изменяется, чтобы отразиться в наборе данных адаптера, как показано ниже:

            override fun areContentsTheSame(
            oldHistory: ResolvedAddress, newHistory: ResolvedAddress): Boolean {
            return oldHistory == newHistory

            // add this if you expect requestedLat to get updated 
            && oldHistory.requestedLat == newHistory.requestedLat

            // add this if you expect requestedLon to get updated 
            && oldHistory.requestedLon == newHistory.requestedLon

            // add this if you expect requestedAddress to get updated 
            && oldHistory.requestedAddress == newHistory.requestedAddress

            // add this if you expect lat to get updated 
            && oldHistory.lat == newHistory.lat


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