RecyclerView.ViewHolder - getLayoutPosition vs getAdapterPosition

88

Depuis la nouvelle version de la bibliothèque de support (22.x), la getPosition()méthode de la RecyclerView.ViewHolderclasse a été déconseillée au lieu des méthodes mentionnées dans la rubrique. Je ne comprends pas vraiment la différence en lisant les documents. Quelqu'un pourrait-il expliquer la différence en termes simples?

J'ai le cas d'utilisation suivant - je donne à mon adaptateur un List, et je souhaite également pouvoir associer des informations supplémentaires pour chaque élément de la liste. J'ai un mappage position-à-extra, et le mappage est disponible pour les détenteurs afin qu'ils puissent récupérer le supplément pour leur position et faire des choses avec. Dans le support, quelle méthode dois-je utiliser?

Que se passe-t-il avec les positions de détenteur lorsque les éléments de liste aux indices 0 et 1 sont échangés? Que renvoient les méthodes?

wujek
la source

Réponses:

115

C'est une situation délicate, désolé que les documents ne soient pas suffisants.

Lorsque le contenu de l'adaptateur change (et que vous appelez notify***()), RecyclerView demande une nouvelle disposition. À partir de ce moment, jusqu'à ce que le système de mise en page décide de calculer une nouvelle mise en page (<16 ms), la position de mise en page et la position de l'adaptateur peuvent ne pas correspondre car la mise en page n'a pas encore reflété les changements d'adaptateur.

Dans votre cas d'utilisation, puisque vos données sont liées au contenu de votre adaptateur (et je suppose que les données sont modifiées en même temps avec les changements d'adaptateur), vous devriez utiliser adapterPosition.

Attention cependant, si vous appelez notifyDataSetChanged(), car il invalide tout, RecyclerView ne sait pas que la position de l'adaptateur de ViewHolder jusqu'à ce que la disposition suivante soit calculée. Dans ce cas, getAdapterPosition()retournera RecyclerView#NO_POSITION(-1 ).

Mais disons que si vous avez appelé notifyItemInserted(0), le getAdapterPosition()de ViewHolder qui était auparavant à la position 0commencera à revenir 1immédiatement. Ainsi, tant que vous distribuez des événements de notification granulaires, vous êtes toujours en bon état (nous connaissons la position de l'adaptateur même si la nouvelle mise en page n'est pas encore calculée).

Un autre exemple, si vous faites quelque chose sur le clic de l'utilisateur, s'il getAdapterPosition()retourne NO_POSITION, il est préférable d'ignorer ce clic parce que vous ne savez pas sur quel utilisateur a cliqué (sauf si vous avez un autre mécanisme, par exemple des identifiants stables pour rechercher l'élément).

Modifier pour lorsque la position de la disposition est bonne

Disons que vous utilisez LinearLayoutManageret souhaitez accéder au ViewHolder au-dessus de l'élément actuellement cliqué. Dans ce cas, vous devez utiliser la position de mise en page pour obtenir l'élément ci-dessus.

mRecyclerView.findViewHolderForLayoutPosition(myViewHolder.getLayoutPosition() - 1)

Vous devez utiliser la position de la mise en page car elle correspond à ce que l'utilisateur voit actuellement à l'écran.

yigit
la source
1
J'ai joué un peu et il s'avère que la méthode getAdapterPosition () renvoie toujours -1 sur moi. Je l'ai débogué et la raison en est que le code de la méthode (final ViewParent parent = itemView.getParent (); if (! (Parent instanceof RecyclerView)) {return -1;} entre toujours dans le bloc if, c'est-à-dire la vue du recycleur n'est pas le parent de ma vue de cellule. Comment cela peut-il être? Mon code pour créer le support est: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()). inflate (R.layout.test_list_item, viewGroup, false)); (Suite dans un autre commentaire.)
wujek
Quand je change le code pour appeler inflate (R.layout.test_list_item, viewGroup, true); (notez le vrai pour 'attacher à la racine), Android lance: java.lang.IllegalStateException: l'enfant spécifié a déjà un parent. Vous devez d'abord appeler removeView () sur le parent de l'enfant. Alors, quelle est la manière correcte de créer un support de vue avec une vue correctement attachée à la vue de l'outil de recyclage? Curieusement, même si getAdapterPosition () renvoie -1 parce que le parent est nul, tout le reste fonctionne correctement.
wujek
1
Le paramètre booléen de l'inflateur de mise en page est "addToParent". Il doit être faux car il est de la responsabilité de LayoutManager de l'ajouter. Je pense que vous appelez getAdapterPosition dans onBind où vous avez déjà passé la position. Techniquement, le détenteur de la vue représente cette position après le retour de onBind. Btw, nous avons mis à jour la position de getAdapter pour renvoyer une position valide (si possible) même si elle est détachée, elle sera bientôt publiée.
yigit
Vous avez raison, j'appelle getAdapterPosition dans onBind. Que dois-je faire dans ce cas à la place, j'ai besoin de la position pour obtenir des informations qui influencent l'état de certaines vues. L'appel de getLayoutPosition dans ce cas est-il correct?
wujek
5
Vous devez utiliser le paramètre de position qui est passé à la méthode onBind.
yigit
2


Afin de soutenir la différence (s) de getAdapterPosition(), getLayoutPosition()et également position; nous remarquerons les cas ci-dessous:

1. positionargument dans la onBindViewHolder()méthode:

Nous pouvons utiliser positionpour lier les données à la vue et il est correct d'utiliser l' positionargument pour ce faire, mais ce n'est pas bien d'utiliser l' positionargument pour gérer les clics des utilisateurs et si vous l'avez utilisé, vous verrez un avertissement vous indiquant "ne pas traiter positioncomme fixe et utiliser à la holder.getAdapterPosition()place ".

2 getAdapterPosition().:

Cette méthode consiste toujours en la position de l'adaptateur mis à jour du holder. Cela signifie que chaque fois que vous avez cliqué sur un élément, vous demandez à l'adaptateur à ce sujet position. vous obtiendrez donc la dernière position de cet élément en termes de logique d'adaptateur.

3 getLayoutPosition().:

Parfois, il est nécessaire de trouver le positionen termes de mise en page mise à jour (la dernière mise en page passée que l'utilisateur voit maintenant), par exemple: Si l'utilisateur demande la troisième, positionil peut voir et vous utilisez swipe/ dismisspour des éléments ou appliquez une animation ou des décorations pour les objets, il sera préférable d'utiliser à la getLayoutPosition()place de getAdapterPosition(), car vous serez toujours sûr que vous traitez avec la position des éléments en termes de la dernière mise en page passée.


Pour plus d'informations à ce sujet; voir ici . . .

elyar abad
la source