Erhalten Sie sichtbare Elemente in RecyclerView

Antworten:

578

Das erste / letzte sichtbare Kind hängt von der LayoutManager. Wenn Sie LinearLayoutManageroder verwenden GridLayoutManager, können Sie verwenden

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

Beispielsweise:

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

Denn LinearLayoutManagerfirst / last hängt von der Adapterbestellung ab. Fragen Sie keine Kinder ab RecyclerView; LayoutManagerMöglicherweise ist es vorzuziehen, mehr Elemente als für das Caching sichtbar zu gestalten.

Yigit
quelle
10
Gibt es eine Möglichkeit, über RecyclerView.Adapter auf diese Methoden zuzugreifen, ohne auf LayoutManager zu verweisen?
Yorgi
3
Wie @Yorgi - im Adapter scheint nützlich. Ich frage mich, ob es ein Muster gibt, das Sie entwickeln können, wenn Sie onBindViewHolder verwenden, um zu verfolgen, welche tatsächlich auf dem Bildschirm angezeigt werden, während ein Benutzer einen Bildlauf durchführt.
RoundSparrow Hilltx
7
getItemCountkann 1 und findFirstVisibleItemPosition-1 bei demselben Anruf zurückgeben.
mbmc
4
Was können Sie über StaggeredLayoutManager vorschlagen?
Hornet2319
37
Diese Methoden sind so unzuverlässig, dass sie völlig nutzlos sind. Besonders nach notifyDataSetChanged / itemRemoved / RangeChanged
JK
12

Schließlich fand ich eine Lösung, um anhand des onBindViewHolder-Ereignisses im Adapter zu ermitteln, ob das aktuelle Element sichtbar ist.

Der Schlüssel ist die Methode isViewPartiallyVisible von LayoutManager.

In Ihrem Adapter können Sie den LayoutManager aus der RecyclerView abrufen , die Sie als Parameter aus dem Ereignis onAttachedToRecyclerView erhalten .

Ernesto Vega
quelle
Aber wo würden Sie diese Methode nennen, bei der der Layout-Manager nicht null ist?
6rchid
Sie müssen auf das Ereignis onAttachedToRecyclerView des Adapters warten. In diesem Fall erhalten Sie die RecyclerView als Parameter. Anschließend können Sie den LayoutManager aus RecyclerVie abrufen und in einer globalen Variablen speichern. Wenn der LayoutManager null ist, kann dies wahrscheinlich daran liegen, dass er der RecyclerView in Activity / Fragment / Layout nicht zugewiesen wurde. und versuchen Sie, den Adapter nach dieser Zuweisung zu erstellen.
Ernesto Vega
Laut Quellcode ist isViewPartiallyVisible stark beschädigt. Wenn vollständig sichtbar ist, wird true zurückgegeben, wenn die Ansicht vollständig sichtbar ist. Bei false wird jedoch nur das Ergebnis negiert, das true zurückgibt, wenn die Ansicht teilweise sichtbar oder nicht sichtbar ist. Auch die Dokumentation macht keinen Sinn.
Molanda
9

Für diejenigen, die eine Logik haben, die im RecyclerView-Adapter implementiert werden soll, können Sie weiterhin den @ vernesto-Ansatz in Kombination mit einem on scrollListener verwenden, um zu erfahren, what you wantwie RecyclerView konsultiert wird. Im Adapter befindet sich etwa Folgendes:

@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
quelle
7

Sie können recyclerView.getChildAt()jedes sichtbare convertview.setTag(index)untergeordnete Element abrufen. Wenn Sie in dieser Ansicht im Adaptercode ein Tag festlegen, können Sie es mit den Adapterdaten verknüpfen.

Sreejith B Naick
quelle
Das Problem mit getChildAt () ist, dass die Reihenfolge der Elemente aus 5 Elementen bestehen kann: 0, 5, 4, 3, 2, 1 (die Anzahl der untergeordneten Elemente ist meistens> = die Anzahl der sichtbaren Elemente). Also habe ich versucht, die Reihenfolge des Elements über getGlobalVisibleRect zu ermitteln.
rekire
Was ist Ihre tatsächliche Anforderung, sichtbares Kind (Kind innerhalb des Bildschirms gebunden) in der tatsächlichen Reihenfolge zu bekommen?
Sreejith B Naick
Jetzt möchte ich wissen, welches Element sich in der Mitte befindet, später interessieren mich vielleicht auch die Grenzen.
rekire
1
Sie erhalten nur sichtbare Elemente von recyclerView.getChildAt(), so funktioniert das im Allgemeinen RecyclerView. RecyclerViewwird versuchen, nur wenige untergeordnete Ansichten zu speichern, die derzeit sichtbar sind (dh innerhalb der Bildschirmgrenzen, nicht Sichtbarkeit als GEGANGEN, UNSICHTBAR), und versuchen, diese Ansichten zu recyceln, wenn der Benutzer einen Bildlauf durchführt.
Sreejith B Naick
5
View visibleChild = recyclerView.getChildAt (0); int positionOfChild = recyclerView.getChildAdapterPosition (visibleChild);
Kevin Lee
4

Die folgenden Linear / Grid LayoutManagerMethoden können verwendet werden, um zu überprüfen, welche Elemente vorhanden sindvisible

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

Wenn Sie is item visible on screeneinen bestimmten Schwellenwert ermitteln möchten, können Sie auf den folgenden Blog verweisen.

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

Sumit Jain
quelle
4

Nachtrag :

Die vorgeschlagenen Funktionen findlast ... Position () nicht nicht korrekt funktionieren in einem Szenario mit einer kollabierenden Symbolleiste , während die Symbolleiste erweitert wird.

Es scheint, dass die Recycler-Ansicht eine feste Höhe hat, und während die Symbolleiste erweitert wird, wird der Recycler teilweise außerhalb des Bildschirms nach unten verschoben. Infolgedessen sind die Ergebnisse der vorgeschlagenen Funktionen zu hoch. Beispiel: Das letzte sichtbare Element soll # 9 sein, aber tatsächlich ist Element # 7 das letzte, das auf dem Bildschirm angezeigt wird.

Dieses Verhalten ist auch der Grund, warum meine Ansicht oft nicht zur richtigen Position gescrollt hat, dh scrollToPosition () hat nicht richtig funktioniert (ich habe die Symbolleiste schließlich programmgesteuert reduziert).

Andreas K. aus M.
quelle
Das stimmt, aber so funktioniert die reduzierte Symbolleiste. Ich habe vor einem Jahr die Entwicklung von Android Apps eingestellt, kann mich aber an diese Tatsache erinnern.
rekire
3

Dafür StaggeredGridLayoutManager:

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

Und um sichtbare Objektansichten zu erhalten:

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

Denken Sie daran, zu überprüfen, ob es leer ist.

Douglas Nassif Roma Junior
quelle
Was meinst du mit "prüfe ob es leer ist"? Wie prüft man?
tmm1
viewsIdsist ein Array und kann leer sein.
Douglas Nassif Roma Junior