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, так как адрес памяти объекта не изменился.
Это несколько советов по отладке, так как трудно дать более четкий ответ без дополнительных знаний о том, как реализован код.
У меня была похожая проблема, но мне удалось ее исправить, обновив существующий элемент новым объектом, а не напрямую обновляя существующий элемент, как предлагается в следующем ответе:
Проблема в том, как 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
}