Получить видимые элементы в RecyclerView


268

Мне нужно знать, какие элементы в настоящее время отображаются в моем RecyclerView. В OnScrollListener.onScroll(...)ListViews нет эквивалента этому методу. Я пытался работать View.getGlobalVisibleRect(...), но этот хак слишком уродлив и не всегда работает.

У кого-нибудь есть идеи?

Ответы:


579

Первый / последний видимый ребенок зависит от 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
Эти методы настолько ненадежны, что совершенно бесполезны. Особенно после notifyDataSetChanged / itemRemoved / RangeChanged
JK

12

Наконец, я нашел решение узнать, является ли текущий элемент видимым, из события onBindViewHolder в адаптере.

Ключевым является метод isViewPartiallyVisible из LayoutManager.

В вашем адаптере вы можете получить LayoutManager из RecyclerView, который вы получаете в качестве параметра из события onAttachedToRecyclerView .


Но где бы вы назвали этот метод, в котором менеджер макета не является нулевым?
6

Вам нужно дождаться события onAttachedToRecyclerView адаптера. В этом случае вы получаете RecyclerView в качестве параметра; а затем вы можете получить LayoutManager из RecyclerVie и сохранить его в глобальном var. Если LayoutManager имеет значение null, возможно, это связано с тем, что он не был назначен RecyclerView в Activity / Fragment / Layout; и попробуйте собрать адаптер после этого назначения.
Эрнесто Вега

Согласно исходному коду, isViewPartiallyVisible сильно сломан. Если свойство completeVisible имеет значение true, оно вернет значение true, если представление полностью видно. Однако, если false, он просто отменяет результат, который возвращает true, если представление является частично видимым или невидимым. Даже документация не имеет смысла.
Моланда


9

для тех, у кого есть логика, которая будет реализована внутри адаптера RecyclerView, вы все равно можете использовать подход @ernesto в сочетании с on scrollListener, чтобы получить доступ к what you wantRecyclerView. Внутри адаптера у вас будет что-то вроде этого:

@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, INVISIBLE), и попытаться перезапустить эти представления при прокрутке пользователя.
Sreejith B Naick

6
View visibleChild = recyclerView.getChildAt (0); int positionOfChild = recyclerView.getChildAdapterPosition (visibleChild);
Кевин Ли

5

Приложение :

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

Кажется, что вид рециркулятора имеет фиксированную высоту, и хотя панель инструментов развернута, рециркулятор перемещается вниз, частично за пределы экрана. Как следствие, результаты предложенных функций слишком высоки. Пример: последний видимый элемент называется # 9, но на самом деле элемент # 7 является последним, который отображается на экране.

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


Это правда, но именно так работает сворачивающаяся панель инструментов. Я прекратил разработку приложений для Android год назад, но я помню этот факт.
rekire

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


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.