RecyclerView прокрутка вверх с AsyncListDiffer не работает
Я использую RecyclerView
с AsyncListDiffer
(рассчитывает и анимирует различия между старыми и новыми элементами, все в фоновом потоке).
У меня есть кнопка для сортировки списка. После того, как я сортирую это и повторно устанавливаю это к RecyclerView
с помощью mDiffer.submitList(items);
Я тоже звоню recyclerView.scrollToPosition(0)
или же (smoothScrollToPosition(0)
), но это не имеет никакого эффекта.
Я думаю, что такое поведение ожидается, так как AsyncListDiffer
вероятно, все еще вычисляет различия в то время, когда scrollToPosition(0)
называется, так что это не имеет никакого эффекта. Дополнительно по умолчанию AsyncListDiffer
не выполняет прокрутку вверх, но вместо этого сохраняет RecyclerView в том же состоянии.
Но как мне сказать RecyclerView
прокрутить вверх после AsyncListDiffer
сделано и обновляется?
4 ответа
Здесь ответили:
В принципе, если вы отправите один и тот же список с другим порядком, он будет проигнорирован. Итак, сначала вам нужно submit(null)
и затем отправьте свой переупорядоченный список.
Я обеспокоен тем, что пока .submitList(null)
возможно, сработал для вас, он только обновил весь 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
,
Если вы посмотрите на javadocs для AsyncListDiffer
с submitList
, вы заметите, что второй параметр - это Runnable
который выполняется после отправки элементов в адаптер:
/**
* Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
* thread.
* <p>
* If a List is already present, a diff will be computed asynchronously on a background thread.
* When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
* and the new List will be swapped in.
* <p>
* The commit callback can be used to know when the List is committed, but note that it
* may not be executed. If List B is submitted immediately after List A, and is
* committed directly, the callback associated with List A will not be run.
*
* @param newList The new List.
* @param commitCallback Optional runnable that is executed when the List is committed, if
* it is committed.
*/
Итак, что вы хотите, это (это, кстати, в Котлине):
adapter.submitList(items) {
// This will run after items have been set in the adapter
recyclerView.scrollToPosition(0)
}
или в Java
adapter.submitList(items, () -> {
recyclerView.scrollToPosition(0);
});
Используйте registerAdapterDataObserver для адаптера в действии сразу после кода «mRecyclerView.setAdapter(adapter)»:
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
public void onChanged() {
mRecyclerView.scrollToPosition(0);
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// same or similar scroll code
}
}
});
Затем отмените регистрацию наблюдателя в onStop() в действии.