Uzyskaj widoczne elementy w RecyclerView

Odpowiedzi:

578

Pierwsze / ostatnie widoczne dziecko zależy od LayoutManager. Jeśli używasz LinearLayoutManagerlub GridLayoutManager, możesz użyć

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

Na przykład:

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

Dla LinearLayoutManagerpierwszego / ostatniego zależy od zamówienia adaptera. Nie pytaj dzieci od RecyclerView; LayoutManagermoże preferować układanie większej liczby elementów niż jest widoczne do buforowania.

Yigit
źródło
10
Czy jest jakaś możliwość dostępu do tych metod z RecyclerView.Adapter bez przekazywania odniesienia do LayoutManager?
Yorgi,
3
Jak @Yorgi - wewnątrz adaptera wydaje się przydatny. Zastanawiam się, czy istnieje jakiś wzorzec, który można rozwinąć za pomocą onBindViewHolder do śledzenia, które z nich są na ekranie podczas przewijania użytkownika.
RoundSparrow hilltx
7
getItemCountmoże zwrócić 1 i findFirstVisibleItemPosition-1 dla tego samego połączenia.
mbmc
4
co możesz zasugerować na temat StaggeredLayoutManager?
hornet2319,
37
Metody te są tak zawodne, że są całkowicie bezużyteczne. Zwłaszcza po powiadomieniuDataSetChanged / itemRemoved / RangeChanged
JK
12

Wreszcie znalazłem rozwiązanie pozwalające stwierdzić, czy bieżący element jest widoczny, na podstawie zdarzenia onBindViewHolder w adapterze.

Kluczem jest metoda isViewPartIALVisible z LayoutManager.

W adapterze możesz pobrać LayoutManager z RecyclerView, który otrzymasz jako parametr ze zdarzenia onAttachedToRecyclerView .

Ernesto Vega
źródło
Ale gdzie nazwałbyś tę metodę, w której menedżer układu nie jest pusty?
6rchid
Musisz poczekać na zdarzenie onAttachedToRecyclerView adaptera. W takim przypadku otrzymasz RecyclerView jako parametr; a następnie możesz pobrać LayoutManager z RecyclerVie i zapisać go w zmiennej globalnej. Jeśli LayoutManager ma wartość NULL, prawdopodobnie może to wynikać z tego, że nie został przypisany do RecyclerView w Activity / Fragment / Layout; i spróbuj zbudować adapter po tym przypisaniu.
Ernesto Vega
Według kodu źródłowego isViewPartIALVisible jest poważnie uszkodzony. Jeśli parametr trueVisible jest prawdziwy, zwróci wartość true, jeśli widok jest w pełni widoczny. Jednak jeśli jest fałszywy, to po prostu neguje wynik, który zwraca true, jeśli widok jest częściowo widoczny lub niewidoczny. Nawet dokumentacja nie ma sensu.
Molanda,
9

dla tych, którzy mają logikę, która ma zostać zaimplementowana w adapterze RecyclerView, możesz nadal korzystać z podejścia @ernesto w połączeniu z funkcją scrollListener, aby uzyskać dostęp what you wantdo RecyclerView. Wewnątrz adaptera znajdziesz coś takiego:

@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
źródło
7

Możesz użyć, recyclerView.getChildAt()aby uzyskać każde widoczne dziecko, a ustawienie tagu convertview.setTag(index)w tym widoku w kodzie adaptera pomoże ci powiązać go z danymi adaptera.

Sreejith B Naick
źródło
Problem z getChildAt () polega na tym, że kolejność elementów może wynosić 5 elementów: 0, 5, 4, 3, 2, 1 (liczba potomków to w większości> = liczba widocznych elementów). Próbowałem więc uzyskać kolejność elementu za pośrednictwem getGlobalVisibleRect.
rekire
Jakie jest Twoje rzeczywiste wymaganie, aby widoczne dziecko (dziecko wewnątrz ekranu było powiązane) w rzeczywistej kolejności?
Sreejith B Naick
Teraz chcę wiedzieć, który element znajduje się w środku, później może też interesują mnie granice.
rekire
1
Dostaniesz tylko widoczne przedmioty recyclerView.getChildAt(), tak na ogół RecyclerViewdziała. RecyclerViewspróbuje zatrzymać tylko kilka widoków potomnych, które są obecnie widoczne (tj. w granicach ekranu, a nie Widoczność jako BRAK, NIEWIDOCZNE) i spróbuje przetworzyć te widoki, gdy użytkownik przewinie.
Sreejith B Naick
6
Wyświetl visibleChild = recyclinglerView.getChildAt (0); int positionOfChild = recyclerView.getChildAdapterPosition (visibleChild);
Kevin Lee
4

Linear / Grid LayoutManagerAby sprawdzić, które elementy są, można zastosować następujące metodyvisible

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

a jeśli chcesz śledzić is item visible on screendla pewnego progu, możesz odnieść się do następującego bloga.

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

Sumit Jain
źródło
4

Uzupełnienie :

Proponowane funkcje findLast ... position () ma nie działać prawidłowo w scenariuszu z paska narzędzi zapadającego natomiast pasek narzędzi jest rozszerzony.

Wygląda na to, że widok recyklera ma stałą wysokość i podczas gdy pasek narzędzi jest rozwinięty, recykler jest przesuwany w dół, częściowo poza ekran. W konsekwencji wyniki proponowanych funkcji są zbyt wysokie. Przykład: Mówi się, że ostatnim widocznym przedmiotem jest numer 9, ale w rzeczywistości element 7 jest ostatnim, który jest na ekranie.

To zachowanie jest również przyczyną, dla której mój widok często nie przewijał się do prawidłowej pozycji, tj. ScrollToPosition () nie działał poprawnie (w końcu programowo zwinąłem pasek narzędzi).

Andreas K. aus M.
źródło
To prawda, ale tak działa składany pasek narzędzi. Rok temu przestałem tworzyć aplikacje na Androida, ale pamiętam ten fakt.
rekire
3

Aby StaggeredGridLayoutManagerto zrobić:

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

Aby uzyskać widoczne widoki elementów:

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

Pamiętaj, aby sprawdzić, czy jest pusty.

Douglas Nassif Roma Junior
źródło
Co masz na myśli „sprawdź, czy jest pusty”? Jak się sprawdza?
tmm1
viewsIdsjest tablicą i może być pusta.
Douglas Nassif Roma Junior