web-dev-qa-db-fra.com

Animer RecyclerView lors du défilement

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?

24
rekire

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();
    }
});
9
rekire

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;
6
Vineet Ashtekar

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

0
CrazyFox