Est-il possible d'animer les éléments d'un RecyclerView lorsque je le fais défiler?
J'ai jeté un œil à DefaultItemAnimator
et RecyclerView.ItemAnimator
, mais ces animations ne semblent être appelées que si le jeu de données a changé, corrigez-moi si je me trompe.
Je suis un peu confus au sujet de RecyclerView.ItemAnimator.animateMove()
quand l'appelle-t-il? J'ai mis des points d'arrêt dans cette classe, mais aucun d'entre eux n'arrête mon application.
Mais revenons à ma question, comment puis-je animer le RecyclerView? Je veux que certains éléments ont une autre opacité, dépend de certaines règles personnalisées.
Je me suis dit que le mouvement d’animation est exactement ce que je recherche. Ces méthodes sont appelées à partir de dispatchLayout()
. Voici le javadoc de cette méthode:
Wrapper autour de layoutChildren () qui gère l’animation des modifications causées par layout . Les animations partent de l'hypothèse qu'il y a cinq types d'éléments différents en jeu:
PERSISTENT: les éléments sont visibles avant et après la mise en page
REMOVED: les éléments étaient visibles avant la mise en page et ont été supprimés par l'application
ADDED: les éléments n'existaient pas avant la mise en page et ont été ajoutés par l'application
DISAPPEARING: des éléments existent dans le jeu de données avant/après, mais sont passés de visible à non visible dans le processus de mise en page (ils ont été déplacés de l'écran comme effet secondaire d'autres modifications)
APPEARING: des éléments existent dans le jeu de données avant/après, mais ils sont passés de non visible à visible dans le processus de mise en page (ils ont été déplacés sur l'écran comme effet secondaire d'autres changements)
L’approche globale détermine quels éléments existent avant/après la disposition et déduit l'un des cinq états ci-dessus pour chacun des éléments. Puis les animations sont configurés en conséquence:
Les vues PERSISTENT sont déplacées ({@link ItemAnimator # animateMove (ViewHolder, int, int, int, int)}) Les vues supprimées sont supprimées ({@link ItemAnimator # animateRemove (ViewHolder)})
Les vues ajoutées sont ajoutées ({@link ItemAnimator # animateAdd (ViewHolder)})
Les vues DISPARATIVES sont déplacées de l'écran
Les vues APPEARING sont déplacées à l'écran
Jusqu'à présent, je cherche PERSISTENT, DISAPPEARING et APPEARING, mais ces méthodes ne sont jamais appelées à cause de cette ligne:
boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved
&& !mItemsChanged;
mItemsAddedOrRemoved
est simplement toujours faux, aussi aucun de ces rappels n'est atteint. Avez-vous une idée de comment définir correctement le drapeau?
J'ai fini par utiliser OnScrollListener
et l'animer dans une méthode animate()
personnalisée. Dans mon cas, ce code ne prend que 2 ms, ce qui ne pose aucun problème pour les 60 images par seconde.
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(int newState) {
if(newState == RecyclerView.SCROLL_STATE_IDLE) {
// special handler to avoid displaying half elements
scrollToNext();
}
animate();
}
@Override
public void onScrolled(int dx, int dy) {
animate();
}
});
Je l'ai fait de cette façon. Peut aider quelqu'un. Je ne sais pas si c'est la meilleure façon de le faire mais cela fonctionne bien pour moi.
UPDATE: Pour résoudre le problème du défilement rapide, substituez la méthode onViewDetachedFromWindow
de l'adaptateur et appelez clearAnimation
dans la vue animée (dans ce cas, holder.itemView.clearAnimation()
).
up_from_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:shareInterpolator="@Android:anim/decelerate_interpolator">
<translate
Android:fromXDelta="0%" Android:toXDelta="0%"
Android:fromYDelta="100%" Android:toYDelta="0%"
Android:duration="400" />
</set>
down_from_top.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:shareInterpolator="@Android:anim/decelerate_interpolator">
<translate
Android:fromXDelta="0%" Android:toXDelta="0%"
Android:fromYDelta="-100%" Android:toYDelta="0%"
Android:duration="400" />
</set>
Et enfin, mettez ce code dans onBindViewHolder
de recyclerView
. Créez un champ appelé lastPosition et initialisez-le à -1.
Animation animation = AnimationUtils.loadAnimation(context,
(position > lastPosition) ? R.anim.up_from_bottom
: R.anim.down_from_top);
holder.itemView.startAnimation(animation);
lastPosition = position;
La bonne solution consiste à animer le titulaire dans l’adaptateur de la méthode onBindViewHolder
. Extrait tiré du projet Material Test Slidenerd:
@Override
public void onBindViewHolder(ViewHolderBoxOffice holder, int position) {
Movie currentMovie = mListMovies.get(position);
//one or more fields of the Movie object may be null since they are fetched from the web
holder.movieTitle.setText(currentMovie.getTitle());
//retrieved date may be null
Date movieReleaseDate = currentMovie.getReleaseDateTheater();
if (movieReleaseDate != null) {
String formattedDate = mFormatter.format(movieReleaseDate);
holder.movieReleaseDate.setText(formattedDate);
} else {
holder.movieReleaseDate.setText(Constants.NA);
}
int audienceScore = currentMovie.getAudienceScore();
if (audienceScore == -1) {
holder.movieAudienceScore.setRating(0.0F);
holder.movieAudienceScore.setAlpha(0.5F);
} else {
holder.movieAudienceScore.setRating(audienceScore / 20.0F);
holder.movieAudienceScore.setAlpha(1.0F);
}
if (position > mPreviousPosition) {
AnimationUtils.animateSunblind(holder, true);
} else {
AnimationUtils.animateSunblind(holder, false);
}
mPreviousPosition = position;
voici un link