Отримати видимі елементи в RecyclerView


268

Мені потрібно знати, які елементи відображаються в моєму RecyclerView. Не існує еквівалента OnScrollListener.onScroll(...)методу в ListViews. Я намагався працювати View.getGlobalVisibleRect(...), але цей хак занадто потворний і не завжди також працює.

У когось якісь ідеї?

Відповіді:


578

Перша / остання видима дитина залежить від LayoutManager. Якщо ви використовуєте LinearLayoutManagerабо GridLayoutManager, ви можете використовувати

int findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();

Наприклад:

GridLayoutManager layoutManager = ((GridLayoutManager)mRecyclerView.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();

Тому що LinearLayoutManagerперший / останній залежить від замовлення адаптера. Не запитуйте дітей RecyclerView; LayoutManagerможе вважати за краще розміщувати більше елементів, ніж видимих ​​для кешування.


10
Чи є можливість отримати доступ до цих методів від RecyclerView.Adapter, не передаючи посилання на LayoutManager?
Йоргі

3
Як @Yorgi - всередині адаптера здається корисним. Цікаво, чи є якийсь шаблон, який ви можете розвинути, використовуючи onBindViewHolder для відстеження, які з них насправді на екрані, а користувач прокручує.
RoundSparrow hilltx

7
getItemCountможе повернути 1 і findFirstVisibleItemPosition-1 під час одного дзвінка.
mbmc

4
що ви можете підказати про StaggeredLayoutManager?
hornet2319

37
Ці методи настільки ненадійні, що вони абсолютно марні. Особливо після повідомленняDataSetChanged / itemRemoved / RangeChanged
JK

12

Нарешті, я знайшов рішення дізнатись, чи видно поточний елемент, з події onBindViewHolder в адаптері.

Ключ - метод isViewPartialVisible від LayoutManager.

У своєму адаптері ви можете отримати LayoutManager від RecyclerView, який ви отримаєте як параметр від події onAttachedToRecyclerView .


Але де б ви назвали цей метод, у якому менеджер макетів не є нульовим?
6рхіда

Вам потрібно дочекатися події адаптера onAttachedToRecyclerView. У цьому випадку ви отримуєте RecyclerView як параметр; а потім ви можете отримати LayoutManager від RecyclerVie та зберегти його у глобальній варі. Якщо параметр LayoutManager є недійсним, можливо, це сталося тому, що він не був призначений RecyclerView у діяльності / фрагменті / макеті; і спробуйте скласти адаптер після цього призначення.
Ернесто Вега

Згідно з вихідним кодом, isViewParicallyVisible погано порушений. Якщо trueVisible є істинним, воно повернеться true, якщо вид буде повністю видимим. Однак, якщо помилковий, він просто заперечує результат, який повертає істину, якщо подання частково видиме чи не видно. Навіть документація не має сенсу.
Molanda


9

для тих, хто має логіку, яку потрібно реалізувати всередині адаптера RecyclerView, ви все одно можете використовувати @ernesto підхід у поєднанні з on-scrollListener, щоб отримати what you wantконсультацію щодо RecyclerView. Всередині адаптера у вас буде щось подібне:

@Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if(manager instanceof LinearLayoutManager && getItemCount() > 0) {
            LinearLayoutManager llm = (LinearLayoutManager) manager;
            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                }

                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                        int visiblePosition = llm.findFirstCompletelyVisibleItemPosition();
                        if(visiblePosition > -1) {
                            View v = llm.findViewByPosition(visiblePosition);
                            //do something
                            v.setBackgroundColor(Color.parseColor("#777777"));
                        }
                }
            });
        }
    }

7

Ви можете використовувати recyclerView.getChildAt()для отримання кожної видимої дитини, а встановлення деякого тегу convertview.setTag(index)на цьому перегляді в коді адаптера допоможе вам пов’язати його з даними адаптера.


Проблема з getChildAt () полягає в тому, що порядок елементів може бути з 5 елементів: 0, 5, 4, 3, 2, 1 (дочірнє число в основному> = кількість видимих ​​елементів). Тому я намагався отримати порядок елемента через getGlobalVisibleRect.
rekire

Яка ваша справжня вимога, щоб побачити дитину (дитина всередині екрану) у фактичному порядку?
Sreejith B Naick

Тепер я хочу знати, який елемент знаходиться в центрі, пізніше мене, можливо, також цікавлять межі.
rekire

1
Ви отримаєте лише видимі предмети recyclerView.getChildAt(), ось як це взагалі RecyclerViewпрацює. RecyclerViewнамагатиметься утримувати лише декілька дочірніх переглядів, які є видимими на даний момент (тобто в межах екрана, а не на видимості як GONE, NEVISIBLE) та спробує переробити ці подання, коли користувач прокручує.
Sreejith B Naick,

6
Переглянути visibleChild = recilerView.getChildAt (0); int positionOfChild = recilerView.getChildAdapterPosition (видимийChild);
Кевін Лі

4

Linear / Grid LayoutManagerДля перевірки, які саме елементи, можна використовувати наступні методиvisible

int findFirstVisibleItemPosition();
int findLastVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();

і якщо ви хочете відстежувати is item visible on screenдеякий поріг, тоді ви можете звернутися до наступного блогу.

https://proandroiddev.com/detecting-list-items-perceived-by-user-8f164dfb1d05


4

Додаток :

Пропоновані функції FindLast ... Позиція () нічого НЕ працює належним чином в сценарії з руйнується панелі інструментів в той час як панель інструментів розширюється.

Здається, вигляд рециркулятора має фіксовану висоту, і в той час, як панель інструментів розширюється, утилізатор переміщується вниз, частково поза екраном. Як наслідок, результати запропонованих функцій занадто високі. Приклад: останнім видимим елементом вважається №9, але насправді пункт №7 є останнім, який відображається на екрані.

Така поведінка також є причиною того, що мій погляд часто не вдається прокрутити до правильної позиції, тобто scrollToPosition () не працює належним чином (я нарешті програв панель інструментів програмно).


Це правда, але саме так працює руйнується панель інструментів. Рік тому назад я припинив розробку Android-програм, але пам'ятаю цей факт.
rekire

3

Для StaggeredGridLayoutManagerцього:

RecyclerView rv = findViewById(...);
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(...);
rv.setLayoutManager(lm);

І щоб отримати видимі перегляди предметів:

int[] viewsIds = lm.findFirstCompletelyVisibleItemPositions(null);
ViewHolder firstViewHolder = rvPlantios.findViewHolderForLayoutPosition(viewsIds[0]);
View itemView = viewHolder.itemView;

Не забудьте перевірити, чи він порожній.


Що ви маєте на увазі "перевірити, чи порожній"? Як перевірити?
tmm1

viewsIdsце масив, і він може бути порожнім.
Дуглас Насіф Рома Юніор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.