ListAdapter не обновляет RecyclerView, если я отправляю тот же список с другим порядком элементов

Я использую RecyclerView с ListAdapter (который использует AsyncListDiffer для вычисления и анимации изменений при замене списка).

Проблема в том, что если я submit() некоторый список, затем переупорядочить этот список, и submit() опять ничего не происходит.

Как я заставляю ListAdapter оценить этот новый список, даже если он "тот же" (но порядок изменился)?

Новые выводы:

Я проверил исходный код submitList() и в начале есть проверка:

public void submitList(@Nullable final List<T> newList) {
        final int runGeneration = ++this.mMaxScheduledGeneration;
        if (newList != this.mList) {

Я думаю, что это проблема. Но как пройти это? Я имею в виду, наверняка разработчики подумали о представлении другого упорядоченного списка?

8 ответов

Решение

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

@Override
public void submitList(@Nullable final List<T> list) {
super.submitList(list != null ? new ArrayList<>(list) : null);
}

Таким образом, чтобы решить эту проблему, вам нужно вызвать эту функцию с null Сначала сразу звоните с измененным списком заказов.

submitList(null);
submitList(orderChangedList);

Вместо того

submitList(mySameOldListThatIModified)

Вы должны отправить новый список, например:

ArrayList newList = new ArrayList(oldList);
newList.add(somethingNew); // Or sort or do whatever you want
submitList(newList);

Это вроде проблема с API. Мы ожидаем, что ListAdapter сохранит копию списка, но это не так, вероятно, по причинам памяти. Когда вы меняете свой старый список, вы фактически меняете тот же список, который сохранил ListAdapter. Когда ListAdapter проверяетif (newList != this.mList)и то и другое newList а также mList относятся к одному и тому же экземпляру списка, поэтому независимо от того, что вы изменили в этом списке, он будет равен самому себе и проигнорирует ваше обновление.

В kotlin вы можете создать новый список с помощью:

val newList = oldList.toList() // Unintuitive way to copy a list
newList.first() = newList.first().copy(isFavourite = false) // Do whatever modifications you want
submitList(newList)

Учтите, что вы не можете этого сделать:

newList.first().isFavourite = false

потому что это также изменит первый элемент в вашем старом списке, и снова ListAdapterне увидит разницы между первым элементом в вашем старом списке и первым элементом в вашем новом списке. Я бы рекомендовал, чтобы все позиции в вашем списке былиval только свойства, чтобы избежать этой проблемы.

Он побеждает цель ListAdapter для автоматического расчета и анимации изменений списка, когда вы вызываете эти строки последовательно:

submitList(null);
submitList(orderChangedList);

Значит, ты только очистил (null) текущий список ListAdapter, а затем отправлен (.submitList()) новый список. Таким образом, соответствующая анимация в этом случае не будет видна, а будет только обновление всего RecyclerView.

Решение заключается в реализации .submitList( List<T> list) метод внутри вашего ListAdapter следующим образом:

public void submitList(@Nullable List<T> list) {
    mDiffer.submitList(list != null ? new ArrayList<>(list) : null);
}

Таким образом, вы позволяете ListAdapter сохранять свой currentList и "расставлять" его с newList, тем самым вычисляя анимацию, в отличие от "расхождения" с null,

Примечание. Однако анимация обновления не произойдет, если, конечно, newList содержит те же элементы в том же порядке, что и originalList.

У меня была аналогичная проблема, но неправильное отображение было вызвано комбинацией setHasFixedSize (true) и android:layout_height="wrap_content". Впервые адаптер был снабжен пустым списком, поэтому высота никогда не обновлялась и была равна 0. В любом случае, это решило мою проблему. У кого-то может быть такая же проблема, и он подумает, что проблема в адаптере.

Пример listadapter для android recyclerview, RecyclerView ListAdapter. ListAdapter - это адаптер RecyclerView, отображающий список. Это доступно в RecyclerView 27.1+, и такая же функция также существует в классе AsyncListDiffer, если вы не можете расширить адаптер. ListAdapter помогает работать с RecyclerViews, которые со временем изменяют содержимое. usersList.observe(это, список -> adapter.submitList(список)); recyclerView.setAdapter(адаптер); }} класс UserAdapter расширяет ListAdapter<User,

все кредиты этому человеку:https://www.xspdf.com/help/50031492.html

Чтобы добавить к ответу Карсона, что это лучший способ сделать это, вы можете сохранить преимущества submitList в Котлине:

submitList(oldList.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
})

Если у вас включен setHasFixedSize(true)удалите эту строку.

«RecyclerView может выполнить несколько оптимизаций, если он заранее знает, что на размер RecyclerView не влияет содержимое адаптера ....»

Проблема в том, что новый представленный список не отображается.

      androidx.recyclerview:recyclerview:1.2.0-beta02

Временное решение - плавная прокрутка к любой позиции после фиксации нового списка, я делаю это наверху.

      movieListAdapter.submitList(list) {
    binding.recycler.smoothScrollToPosition(0)
}

Просто позвони listAdapter.notifyDataSetChanged() и ListAdapter перерисует список на основе представленных значений.

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