RecyclerView прокрутка вверх с AsyncListDiffer не работает

Я использую RecyclerView с AsyncListDiffer (рассчитывает и анимирует различия между старыми и новыми элементами, все в фоновом потоке).

У меня есть кнопка для сортировки списка. После того, как я сортирую это и повторно устанавливаю это к RecyclerView с помощью mDiffer.submitList(items); Я тоже звоню recyclerView.scrollToPosition(0) или же (smoothScrollToPosition(0)), но это не имеет никакого эффекта.

Я думаю, что такое поведение ожидается, так как AsyncListDiffer вероятно, все еще вычисляет различия в то время, когда scrollToPosition(0) называется, так что это не имеет никакого эффекта. Дополнительно по умолчанию AsyncListDiffer не выполняет прокрутку вверх, но вместо этого сохраняет RecyclerView в том же состоянии.

Но как мне сказать RecyclerView прокрутить вверх после AsyncListDiffer сделано и обновляется?

4 ответа

Решение

Здесь ответили:

/questions/48679553/listadapter-ne-obnovlyaet-recyclerview-esli-ya-otpravlyayu-tot-zhe-spisok-s-drug/48679565#48679565

В принципе, если вы отправите один и тот же список с другим порядком, он будет проигнорирован. Итак, сначала вам нужно 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() в действии.

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