web-dev-qa-db-fra.com

RecyclerView Adapter notifyDataSetChanged arrête une animation de fantaisie

Je construis un composant basé sur RecyclerView, permettant à l'utilisateur de réorganiser les éléments par glisser-déposer . Une fois que je suis sur le côté DragListener, j'ai besoin de la position qu'il a dans l'adaptateur pour effectuer le bon déplacement, mais je n'ai accès à la vue . Voici donc ce que je fais dans la liaison de vue de l'adaptateur:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    Track track = mArray.get(position);
    viewHolder.itemView.setTag(R.string.TAG_ITEM_POSITION, position);
}

Cela vous semble-t-il correct? Parce que si je déplace un objet comme celui-ci:

public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
}

alors la balise de position n’est plus correcte, et si j’ai notifyDataSetChanged (), je perds l’animation de fantaisie . Une suggestion?

28
elgui

Non c'est faux. Tout d’abord, vous ne pouvez pas faire référence à la position transmise au onBindViewHolder après le retour de cette méthode. RecyclerView ne réassociera pas une vue lorsque sa position changera (en raison du déplacement d’éléments, etc.).

Au lieu de cela, vous pouvez utiliser ViewHolder#getPosition() qui vous renverra la position mise à jour.

Si vous corrigez cela, votre code de déménagement devrait fonctionner et fournir des animations Nice.

L'appel de notifyDataSetChanged empêchera les animations prédictives, évitez-le aussi longtemps que vous le pouvez. Voir documentation pour plus de détails.

Edit (from comment): pour obtenir la position de l'extérieur, obtenez le détenteur de la vue enfant de recyclerview, puis la position de vh. Voir api RecyclerView pour plus de détails

5
yigit

Il existe un moyen de conserver des animations fantaisistes avec seulement notifyDataSetChanged()

  1. Vous devez créer votre propre variable GridLayoutManager avec la méthode supportsPredictiveItemAnimations() overriden retournée true;

  2. Vous devez mAdapter.setHasStableIds(true)

  3. La partie que je trouve délicate est que vous devez remplacer la méthode getItemId() de votre adaptateur. Il doit renvoyer une valeur qui est vraiment unique et non une fonction directe de position. Quelque chose comme mItems.get(position).hashCode()

A parfaitement fonctionné dans mon cas - de superbes animations pour ajouter, supprimer et déplacer des éléments uniquement à l'aide de notifyDataSetChanged()

100
tochkov

1) Vous utiliserez notifyItemInserted(position); ou notifyItemRemoved(position); au lieu de notifyDataSetChanged() pour l'animation . 2) Vous pouvez simplement résoudre votre problème manuellement - en utilisant 

public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
    ViewHolder fromHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(from);
    ViewHolder toHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(to);
    Tag fromTag = fromHolder.itemView.getTag();
    fromHolder.itemView.setTag(toHolder.itemView.getTag()); 
    toHolder.itemView.setTag(fromTag);

}
3
Rahim Rahimov

Vous devez déplacer votre méthode sur OnCreateViewHolder, puis notifyItemRemoved(index) fonctionne correctement.

0
Dominik

Je suis capable de gérer les animations tactiles en l'ajoutant à l'élément externe de mon élément de liste

<View
    Android:foreground="?android:attr/selectableItemBackground"
...>
0
fix