Я использую новую библиотеку поддержки ListAdapter
. Вот мой код для адаптера
class ArtistsAdapter : ListAdapter<Artist, ArtistsAdapter.ViewHolder>(ArtistsDiff()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(parent.inflate(R.layout.item_artist))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(artist: Artist) {
itemView.artistDetails.text = artist.artistAlbums
.plus(" Albums")
.plus(" \u2022 ")
.plus(artist.artistTracks)
.plus(" Tracks")
itemView.artistName.text = artist.artistCover
itemView.artistCoverImage.loadURL(artist.artistCover)
}
}
}
Я обновляю адаптер с помощью
musicViewModel.getAllArtists().observe(this, Observer {
it?.let {
artistAdapter.submitList(it)
}
})
Мой класс diff
class ArtistsDiff : DiffUtil.ItemCallback<Artist>() {
override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem?.artistId == newItem?.artistId
}
override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
return oldItem == newItem
}
}
Что происходит, когда submitList вызывается в первый раз, когда адаптер отображает все элементы, но когда submitList вызывается снова с обновленными свойствами объекта, он не повторно отображает измененное представление.
Он повторно отображает представление, когда я прокручиваю список, который, в свою очередь, вызывает bindView()
Кроме того, я заметил, что вызов adapter.notifyDatasSetChanged()
после отправки списка отображает представление с обновленными значениями, но я не хочу вызывать, notifyDataSetChanged()
потому что адаптер списка имеет встроенные утилиты diff.
Кто-нибудь может мне здесь помочь?
android
kotlin
listadapter
Виреш Чарантимат
источник
источник
ArtistsDiff
реализациейArtist
самой себя.Ответы:
Изменить: я понимаю, почему это происходит, это не моя точка зрения. Я хочу сказать, что он должен, по крайней мере, выдать предупреждение или вызвать
notifyDataSetChanged()
функцию. Потому что, видимо, я вызываюsubmitList(...)
функцию не просто так. Я почти уверен, что люди часами пытаются выяснить, что пошло не так, пока не поймут, что submitList () молча игнорирует вызов.Это из-за
Google
странной логики. Поэтому, если вы передадите тот же список адаптеру, он даже не вызоветDiffUtil
.public void submitList(final List<T> newList) { if (newList == mList) { // nothing to do return; } .... }
Я действительно не понимаю всего этого,
ListAdapter
если он не может обрабатывать изменения в том же списке. Если вы хотите изменить элементы в списке, который вы передаете,ListAdapter
и увидеть изменения, вам нужно либо создать глубокую копию списка, либо вам нужно использовать обычныйRecyclerView
с вашим собственнымDiffUtill
классом.источник
submitList
, верно? Он должен, по крайней мере, вызвать,notifyDataSetChanged()
вместо того, чтобы молча игнорировать вызов. Я почти уверен, что люди часами пытаются понять, что пошло не так, пока неsubmitList()
поймут, что звонок игнорируется.RecyclerView.Adapter<VH>
иnotifyDataSetChanged()
. Жизнь сейчас хороша. Потраченное впустую много часовnotifyDataSetChanged()
стоит дорого и полностью лишит смысла реализацию на основе DiffUtil. Вы можете быть осторожны и внимательны при вызовеsubmitList
только с новыми данными, но на самом деле это просто ловушка производительности.Библиотека предполагает, что вы используете Room или любой другой ORM, который предлагает новый асинхронный список каждый раз, когда он обновляется, поэтому простой вызов submitList для него будет работать, а для небрежных разработчиков он предотвращает выполнение вычислений дважды, если вызывается один и тот же список.
Принятый ответ правильный, он предлагает объяснение, но не решение.
Что вы можете сделать, если не используете такие библиотеки:
submitList(null); submitList(myList);
Другое решение - переопределить submitList (который не вызывает такого быстрого мигания) как таковой:
@Override public void submitList(final List<Author> list) { super.submitList(list != null ? new ArrayList<>(list) : null); }
Или с кодом Kotlin:
override fun submitList(list: List<CatItem>?) { super.submitList(list?.let { ArrayList(it) }) }
Сомнительная логика, но работает отлично. Я предпочитаю второй метод, потому что он не вызывает вызов onBind для каждой строки.
источник
.submitList(new ArrayList(list))
с Kotlin вам просто нужно преобразовать свой список в новый MutableList, например, этот или другой тип списка в соответствии с вашим использованием
.observe(this, Observer { adapter.submitList(it?.toMutableList()) })
источник
У меня была аналогичная проблема, но неправильный рендеринг был вызван комбинацией
setHasFixedSize(true)
иandroid:layout_height="wrap_content"
. Впервые адаптер поставлялся с пустым списком, поэтому высота никогда не обновлялась0
. Во всяком случае, это решило мою проблему. У кого-то может быть такая же проблема, и он подумает, что проблема в адаптере.источник
Если у вас возникнут проблемы при использовании
recycler_view.setHasFixedSize(true)
вы должны обязательно проверить этот комментарий: https://github.com/oughttbot/expandable-recycler-view/issues/53#issuecomment-362991531
Это решило проблему с моей стороны.
(Вот скриншот комментария по запросу)
источник
Сегодня тоже наткнулся на эту "проблему". С помощью ответа insa_c в и решения RJFares игровая я сделал себе функцию расширения Котлин:
/** * Update the [RecyclerView]'s [ListAdapter] with the provided list of items. * * Originally, [ListAdapter] will not update the view if the provided list is the same as * currently loaded one. This is by design as otherwise the provided DiffUtil.ItemCallback<T> * could never work - the [ListAdapter] must have the previous list if items to compare new * ones to using provided diff callback. * However, it's very convenient to call [ListAdapter.submitList] with the same list and expect * the view to be updated. This extension function handles this case by making a copy of the * list if the provided list is the same instance as currently loaded one. * * For more info see 'RJFares' and 'insa_c' answers on * /programming/49726385/listadapter-not-updating-item-in-reyclerview */ fun <T, VH : RecyclerView.ViewHolder> ListAdapter<T, VH>.updateList(list: List<T>?) { // ListAdapter<>.submitList() contains (stripped): // if (newList == mList) { // // nothing to do // return; // } this.submitList(if (list == this.currentList) list.toList() else list) }
который затем можно использовать где угодно, например:
viewModel.foundDevices.observe(this, Observer { binding.recyclerViewDevices.adapter.updateList(it) })
и он только (и всегда) копирует список, если он такой же, как загруженный в данный момент.
источник
Согласно официальным документам :
Каждый раз, когда вы вызываете submitList, он отправляет новый список для сравнения и отображения.
Вот почему всякий раз, когда вы вызываете submitList в предыдущем (уже представленном списке), он не вычисляет Diff и не уведомляет адаптер об изменении в наборе данных.
источник
Для меня эта проблема возникала, если я использовал
RecyclerView
внутреннюю частьScrollView
сnestedScrollingEnabled="false"
установкой высоты RVwrap_content
.Адаптер обновился правильно, и была вызвана функция привязки, но элементы не были показаны -
RecyclerView
файл застрял в исходном размере.Переход
ScrollView
наNestedScrollView
устраненную проблему.источник
В моем случае я забыл установить
LayoutManager
дляRecyclerView
. Эффект от этого такой же, как описано выше.источник
Для всех, у кого сценарий такой же, как у меня, я оставляю свое решение, которое я не знаю, почему оно работает, здесь.
Решение, которое сработало для меня, было от @Mina Samir, который отправляет список как изменяемый список.
Мой сценарий проблемы:
-Загрузка списка друзей внутри фрагмента.
ActivityMain прикрепляет FragmentFriendList (наблюдает за живыми данными элементов базы данных друга) и в то же время запрашивает http-запрос на сервер, чтобы получить весь мой список друзей.
Обновите или вставьте элементы с http-сервера.
Каждое изменение запускает обратный вызов onChanged для живых данных. Но когда я впервые запускаю приложение, а это значит, что на моем столе ничего не было, submitList завершается успешно без каких-либо ошибок, но на экране ничего не появляется.
Однако когда я запускаю приложение во второй раз, данные загружаются на экран.
Решение, как было сказано выше, заключается в отправке списка как mutableList.
источник
У меня была аналогичная проблема. Проблема была в
Diff
функциях, которые неадекватно сравнивали элементы. Всем, у кого возникла эта проблема, убедитесь, что вашиDiff
функции (и, как следствие, классы объектов данных) содержат правильные определения для сравнения, т.е. сравнение всех полей, которые могут быть обновлены в новом элементе. Например в исходном постеoverride fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem }
Эта функция (потенциально) не делает то, что написано на этикетке: она не сравнивает содержимое двух элементов, если только вы не переопределили
equals()
функцию вArtist
классе. В моем случае у меня этого не было, и определениеareContentsTheSame
только проверило одно из необходимых полей из-за моего недосмотра при его реализации. Это структурное равенство против ссылочного равенства, вы можете найти больше об этом здесьисточник
Мне нужно было изменить мой DiffUtils
override fun areContentsTheSame(oldItem: Vehicle, newItem: Vehicle): Boolean {
Чтобы на самом деле вернуть, является ли содержимое новым, а не просто сравнить идентификатор модели.
источник
Использование первого ответа @RJFares успешно обновляет список, но не поддерживает состояние прокрутки. Все
RecyclerView
начинается с 0-й позиции. В качестве обходного пути я сделал следующее:fun updateDataList(newList:List<String>){ //new list from DB or Network val tempList = dataList.toMutableList() // dataList is the old list tempList.addAll(newList) listAdapter.submitList(tempList) // Recyclerview Adapter Instance dataList = tempList }
Таким образом, я могу поддерживать состояние прокрутки
RecyclerView
вместе с измененными данными.источник