Подтвердить что ты не робот

Есть ли обратный вызов, когда RecyclerView завершил показ своих элементов после того, как я установил его с помощью адаптера?

Фон

Я сделал библиотеку, которая показывает быструю прокрутку для RecyclerView ( здесь, на случай, если кто-то захочет), и я хочу решить, когда показывать и когда скрывать быструю прокрутку.

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

Эта проблема

Я не могу найти прослушиватель/обратный вызов для RecyclerView, чтобы сообщить мне, когда он завершил показ элементов, чтобы я мог проверить, сколько элементов отображается по сравнению с количеством элементов.

RecyclelerView может также изменить свой размер, когда клавиатура появляется и прячется.

Что я пробовал

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

Я мог бы обернуть RecyclerView макетом, который уведомляет меня об изменениях размера, как этот, который я сделал, но я не думаю, что он будет работать, поскольку RecyclerView, вероятно, еще не будет готов сказать, сколько элементов видно,

Способ проверки количества отображаемых элементов может быть использован следующим образом:

    final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
    mRecyclerView.setLayoutManager(layoutManager);
    ...
    Log.d("AppLog", "visible items count:" + (layoutManager.findLastVisibleItemPosition() -layoutManager.findFirstVisibleItemPosition()+1));

Вопрос

Как получить уведомление, когда recyclerView завершил показ своих дочерних представлений, чтобы я мог на основе того, что в данный момент показано, показать/скрыть быструю прокрутку?

4b9b3361

Ответ 1

Я нашел способ решить эту проблему (благодаря пользователю pskink), используя обратный вызов LayoutManager:

final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public void onLayoutChildren(final Recycler recycler, final State state) {
                super.onLayoutChildren(recycler, state);
                //TODO if the items are filtered, considered hiding the fast scroller here
                final int firstVisibleItemPosition = findFirstVisibleItemPosition();
                if (firstVisibleItemPosition != 0) {
                    // this avoids trying to handle un-needed calls
                    if (firstVisibleItemPosition == -1)
                        //not initialized, or no items shown, so hide fast-scroller
                        mFastScroller.setVisibility(View.GONE);
                    return;
                }
                final int lastVisibleItemPosition = findLastVisibleItemPosition();
                int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
                //if all items are shown, hide the fast-scroller
                mFastScroller.setVisibility(mAdapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
            }
        };

Хорошо, что он работает хорошо и справится даже с отображением/скрытием клавиатуры.

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


ОБНОВЛЕНИЕ: есть лучший обратный вызов, который был добавлен позже, который не вызывается несколько раз. Вот новый код вместо того, что я написал выше:

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public void onLayoutCompleted(final State state) {
                super.onLayoutCompleted(state);
                final int firstVisibleItemPosition = findFirstVisibleItemPosition();
                final int lastVisibleItemPosition = findLastVisibleItemPosition();
                int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
                //if all items are shown, hide the fast-scroller
                fastScroller.setVisibility(adapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
            }
        });

Ответ 2

Для этого я использую addOnGlobalLayoutListener. Вот мой пример:

Определение интерфейса для выполнения действия, необходимого после загрузки:

public interface RecyclerViewReadyCallback {
  void onLayoutReady();
}

в RecyclerView я запускаю метод onLayoutReady, когда загрузка готова:

mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
  if (recyclerViewReadyCallback != null) {
    recyclerViewReadyCallback.onLayoutReady();
  }
  recyclerViewReadyCallback = null;
});

Примечание. Значение null необходимо для предотвращения многократного вызова метода.