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

Ответы:

579

Первый / последний видимый ребенок зависит от LayoutManager. Если вы используете LinearLayoutManagerили GridLayoutManager, вы можете использовать

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

Например:

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

Для LinearLayoutManager, первого / последнего зависит от заказа адаптера. Не спрашивайте детей из RecyclerView; LayoutManagerможет предпочесть макетировать больше элементов, чем видно для кэширования.

Yigit
источник
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"));
                        }
                }
            });
        }
    }
Aleyam
источник
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это массив, и он может быть пустым.
Дуглас Нассиф Рома Младший