RecyclerView: найти последний видимый элемент сразу после создания представления

У меня возникла проблема со следующими методами:

int firstVisibleItemPosition = gridLayoutManager.findFirstVisibleItemPosition();
int lastVisibleItemPosition = gridLayoutManager.findLastVisibleItemPosition();

Моя цель: сохранять аналитические данные о том, какие элементы просматривал пользователь.

Чтобы сделать это, я вызываю эти методы в двух разных сценариях:

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

  • когда RecyclerView становится "видимым" для пользователя. теперь, когда проблема начинается. Я ожидаю, что когда фрагмент, содержащий recylcerView, будет передан onResume(), то вызов findLastVisibleItemPosition() вернет видимые элементы. , но в этом случае он возвращает -1. Я предполагаю, что это имеет какое-то отношение к асинхронной загрузке инициализации внутренних элементов recyclerView + адаптера относительно жизненного цикла фрагмента/активности.

отложив этот код на несколько миллисекунд - findLastVisibleItemPosition() возвращает правильные индексы. но я не хочу откладывать жесткую кодировку с помощью обработчика + delayed runnable, потому что планирование замедленного запуска - это работа над тем, что я действительно хочу сделать: обнаруживать, когда просмотр ресайклера заканчивается раздуванием и рисованием на экране всех представлений, которые могут ноги внутри него.

поэтому мои вопросы в основном:

  • Как я могу определить, когда RecyclerView закончил все инициализацию/измерение/раздувание и рисование его дочерних элементов, которые находятся на экране? (перед любым взаимодействием с пользователем..).

  • Есть ли какой-либо надежный способ хорошей практики, чтобы точно знать, какие элементы в пределах 'отображения ресайклеров действительно отображаются на экране?

Ответ 1

Используйте OnChildAttachListener, чтобы определить, когда ресайклеров присоединяет новый держатель. Используйте механизм задержки, чтобы отправить правильные analitycs (\not hardcoded delaybut примерно так:

...
public void run(){
  removeCallbacks()
  postDelay(sendAnalytics(correctInformation),25);
}

Задержка в 25 мс более надежна, а затем прикрепляется к произвольному производителю.

Ответ 2

Как насчет изменений конфигурации? Если пользователь изменяет ориентацию экрана, видимые элементы тоже изменятся. Факт заключается в том, что видимые элементы постоянно изменяются в соответствии с состоянием RecyclerView, загруженными данными, положениями прокрутки и текущей конфигурацией экрана.

Лучшее, что вы, вероятно, можете сделать, это создать класс, который постоянно отслеживает надмножество видимых элементов, заставляя его реализовывать интерфейс ItemDecoration, который вызывается каждый раз, когда RecyclerView получает redrawn на экране и имеет этот компонент периодически отправляет статистику. Вы вернетесь к новому экземпляру RecyclerView при изменении конфигурации (сохраняя его состояние).

Так, например, этот компонент мог отслеживать минимальные и максимальные видимые позиции. Сначала это будет -1 для обоих. Затем после того, как данные будут загружены, и первые элементы будут показаны на экране, ItemDecoration будет вызываться снова, а первое видимое положение позиции теперь будет 0, а последнее видимое положение позиции теперь будет N. После прокрутки значения снова изменятся, Вы должны сохранить минимальное значение FirstVisibleItemPosition и максимальное значение LastVisibleItemPosition, чтобы получить надмножество. После X секунд без изменений или если пользователь перейдет из Activity, вы будете записывать и отправлять эти цифры.

Ответ 3

Что касается ответа на ваш первый вопрос. Лучшее решение для этой проблемы, которое я нашел, это установить прослушиватель разметки в виде ресайклера:

recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        recyclerView.removeOnLayoutChangeListener(this); //remove layout change listener, you want need it anymore
        //do your work here
    }
});

Что касается второго вопроса. Вы можете получитьChildCount() и getChildAt (int), чтобы получить информацию о прикрепленном дочернем представлении в настоящее время к recyclerView, но это не значит, что они видны на экране (они могут быть немного за пределами экрана). Чтобы проверить, видны ли они, см. Android: как проверить, виден ли вид внутри ScrollView?