Obtenez des éléments visibles dans RecyclerView

Réponses:

579

Le premier / dernier enfant visible dépend du LayoutManager. Si vous utilisez LinearLayoutManagerou GridLayoutManager, vous pouvez utiliser

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

Par exemple:

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

Pour LinearLayoutManager, le premier / dernier dépend de la commande de l'adaptateur. Ne pas interroger les enfants de RecyclerView; LayoutManagerpeut préférer mettre en page plus d'éléments que visibles pour la mise en cache.

yigit
la source
10
Existe-t-il une possibilité d'accéder à ces méthodes à partir de RecyclerView.Adapter sans passer de référence à LayoutManager?
Yorgi
3
Comme @Yorgi - à l'intérieur de l'adaptateur semble utile. Je me demande s'il y a un modèle que vous pouvez développer en utilisant onBindViewHolder pour suivre ceux qui sont réellement à l'écran pendant qu'un utilisateur défile.
RoundSparrow hilltx
7
getItemCountpeut renvoyer 1 et findFirstVisibleItemPosition-1 sur le même appel.
mbmc
4
que pouvez-vous suggérer à propos de StaggeredLayoutManager?
hornet2319
37
Ces méthodes sont si peu fiables qu'elles sont totalement inutiles. Surtout après notifyDataSetChanged / itemRemoved / RangeChanged
JK
12

Enfin, j'ai trouvé une solution pour savoir si l'élément actuel est visible, à partir de l'événement onBindViewHolder dans l'adaptateur.

La clé est la méthode isViewPartiallyVisible de LayoutManager.

Dans votre adaptateur, vous pouvez obtenir le LayoutManager à partir de RecyclerView, que vous obtenez comme paramètre à partir du événement onAttachedToRecyclerView .

Ernesto Vega
la source
Mais où appelleriez-vous cette méthode dans laquelle le gestionnaire de disposition n'est pas nul?
6rchid
Vous devez attendre l'événement onAttachedToRecyclerView de l'adaptateur. Dans ce cas, vous recevez le RecyclerView en tant que paramètre; puis vous pouvez obtenir le LayoutManager à partir de RecyclerVie et l'enregistrer dans une variable globale. Si le LayoutManager est nul, cela pourrait probablement être dû au fait qu'il n'a pas été affecté à RecyclerView dans Activity / Fragment / Layout; et essayez de créer l'adaptateur après cette affectation.
Ernesto Vega
Selon le code source, isViewPartiallyVisible est gravement endommagé. Si complètementVisible est vrai, il retournera vrai si la vue est entièrement visible. Cependant, s'il est faux, il annule simplement le résultat qui retourne vrai si la vue est partiellement visible ou non visible. Même la documentation n'a pas de sens.
Molanda
9

pour ceux qui ont une logique à implémenter à l'intérieur de l'adaptateur RecyclerView, vous pouvez toujours utiliser l'approche @ernesto combinée avec un scrollListener pour obtenir what you wantlorsque le RecyclerView est consulté. À l'intérieur de l'adaptateur, vous aurez quelque chose comme ceci:

@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
la source
7

Vous pouvez utiliser recyclerView.getChildAt()pour obtenir chaque enfant visible, et définir une balise convertview.setTag(index)sur cette vue dans le code de l'adaptateur vous aidera à le relier aux données de l'adaptateur.

Sreejith B Naick
la source
Le problème avec getChildAt () est que l'ordre des éléments peut être parmi 5 éléments: 0, 5, 4, 3, 2, 1 (le nombre d'enfants est principalement> = le nombre d'éléments visibles). J'ai donc essayé d'obtenir l'ordre de l'élément via getGlobalVisibleRect.
rekire
Quelle est votre exigence réelle, obtenir un enfant visible (enfant à l'intérieur de l'écran lié) dans l'ordre réel?
Sreejith B Naick
Maintenant je veux savoir quel élément est au centre, plus tard je m'intéresse peut-être aussi aux limites.
rekire
1
Vous n'obtiendrez que des éléments visibles recyclerView.getChildAt(), c'est ainsi que cela RecyclerViewfonctionne généralement . RecyclerViewessaiera de ne conserver que quelques vues enfant qui sont actuellement visibles (c'est-à-dire dans les limites de l'écran, pas la visibilité en tant que disparu, INVISIBLE) et essaiera de recycler ces vues lorsque l'utilisateur défile.
Sreejith B Naick
6
Afficher visibleChild = recyclerView.getChildAt (0); int positionOfChild = recyclerView.getChildAdapterPosition (visibleChild);
Kevin Lee
5

Addendum :

Les fonctions proposées findLast ... Position () ne fonctionnent pas correctement dans un scénario avec une barre d'outils repliée pendant que la barre d'outils est développée.

Il semble que la vue du recycleur ait une hauteur fixe, et tandis que la barre d'outils est développée, le recycleur est déplacé vers le bas, partiellement hors de l'écran. En conséquence, les résultats des fonctions proposées sont trop élevés. Exemple: le dernier élément visible est dit # 9, mais en fait, l'élément # 7 est le dernier qui est à l'écran.

Ce comportement est également la raison pour laquelle ma vue échouait souvent à la position correcte, c'est-à-dire que scrollToPosition () ne fonctionnait pas correctement (j'ai finalement réduit la barre d'outils par programme).

Andreas K. aus M.
la source
C'est vrai, mais c'est ainsi que fonctionne la barre d'outils de réduction. J'ai arrêté de développer des applications Android il y a un an, mais je m'en souviens.
rekire
4

Les Linear / Grid LayoutManagerméthodes suivantes peuvent être utilisées pour vérifier quels éléments sontvisible

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

et si vous souhaitez suivre is item visible on screenun certain seuil, vous pouvez vous référer au blog suivant.

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

Sumit Jain
la source
3

Pour StaggeredGridLayoutManagercela:

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

Et pour obtenir des vues d'objets visibles:

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

N'oubliez pas de vérifier s'il est vide.

Douglas Nassif Roma Junior
la source
Que voulez-vous dire "vérifier s'il est vide"? Comment vérifie-t-on?
tmm1
viewsIdsest un tableau, et il peut être vide.
Douglas Nassif Roma Junior