web-dev-qa-db-fra.com

Comment animer des éléments RecyclerView lorsqu'ils apparaissent

Comment puis-je animer les éléments RecyclerView quand ils apparaissent?

L'animateur d'élément par défaut s'anime uniquement lorsqu'une donnée est ajoutée ou supprimée une fois les données du recycleur définies. Je suis en train de développer de nouvelles applications et je ne sais pas par où commencer.

Des idées comment y parvenir?

203
PaulNunezM

MODIFIER :

Selon la documentation ItemAnimator :

Cette classe définit les animations qui ont lieu sur les éléments lorsque des modifications sont apportées à l'adaptateur.

Donc, à moins que vous ajoutiez vos éléments un par un à votre RecyclerView et que vous actualisiez la vue à chaque itération, je ne pense pas que ItemAnimator soit la solution à votre besoin.

Voici comment animer les éléments RecyclerView lorsqu'ils apparaissent à l'aide d'un CustomAdapter:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
    private Context context;

    // The items to display in your RecyclerView
    private ArrayList<String> items;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        TextView text;
        // You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
        // It's the view that will be animated
        FrameLayout container;

        public ViewHolder(View itemView)
        {
            super(itemView);
            container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
            text = (TextView) itemView.findViewById(R.id.item_layout_text);
        }
    }

    public CustomAdapter(ArrayList<String> items, Context context)
    {
        this.items = items;
        this.context = context;
    }

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        holder.text.setText(items.get(position));

        // Here you apply the animation when the view is bound
        setAnimation(holder.itemView, position);
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(context, Android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }
}

Et votre custom_item_layout ressemblerait à ceci:

<FrameLayout
    Android:id="@+id/item_layout_container"
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content">

    <TextView
        Android:id="@+id/item_layout_text"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:textAppearance="?android:attr/textAppearanceListItemSmall"
        Android:gravity="center_vertical"
        Android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

</FrameLayout>

Pour plus d'informations sur CustomAdapters et RecyclerView, reportez-vous à la section training de la documentation officielle .

Problèmes de défilement rapide

L'utilisation de cette méthode peut entraîner des problèmes de défilement rapide. La vue peut être réutilisée pendant le déroulement de l'animation. Afin d'éviter cela, il est recommandé d'effacer l'animation lorsque celle-ci est détachée.

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
    {
        ((CustomViewHolder)holder).clearAnimation();
    }

Sur CustomViewHolder:

    public void clearAnimation()
    {
        mRootLayout.clearAnimation();
    }

Ancienne réponse:

Jetez un coup d'œil à Le dépôt de Gabriele Mariotti , je suis sûr que vous trouverez ce qu'il vous faut. Il fournit des éléments ItemAnimators simples pour RecyclerView, tels que SlideInItemAnimator ou SlideScaleItemAnimator.

291
MathieuMaree

J'ai animé la fusion des éléments Recyclerview lorsqu'ils apparaissent pour la première fois, comme indiqué dans le code ci-dessous. Cela sera peut-être utile à quelqu'un.

private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    holder.getTextView().setText("some text");

    // Set the view to fade in
    setFadeAnimation(holder.itemView);            
}

private void setFadeAnimation(View view) {
    AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

Vous pouvez également remplacer setFadeAnimation() par la setScaleAnimation() suivante pour animer l'apparence des éléments en les redimensionnant à partir d'un point:

private void setScaleAnimation(View view) {
    ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

Le code ci-dessus présente des défauts dans la mesure où, lorsque vous faites défiler les éléments RecyclerView, les fondus ou les échelles disparaissent toujours. Si vous le souhaitez, vous pouvez ajouter du code pour autoriser l'animation uniquement lors de la création du fragment ou de l'activité contenant la variable RecyclerView (par exemple, obtenir l'heure système lors de la création et autoriser uniquement l'animation pour les premières millisecondes FADE_DURATION).

53
pbm

J'ai créé une animation à partir de pbm's answer with little modification pour que l'animation ne soit exécutée qu'une seule fois 

dans l'autre mot le Animation appear with you scroll down only 

private int lastPosition = -1;

private void setAnimation(View viewToAnimate, int position) {
    // If the bound view wasn't previously displayed on screen, it's animated
    if (position > lastPosition) {
        ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
        viewToAnimate.startAnimation(anim);
        lastPosition = position;
    }
}

et dans onBindViewHolder appeler la fonction 

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

holder.getTextView().setText("some text");

// call Animation function
setAnimation(holder.itemView, position);            
}
21
Basheer AL-MOMANI

Vous pouvez ajouter un attribut Android: layoutAnimation = "@ anim/rv_item_animation" à RecyclerView comme suit:

<Android.support.v7.widget.RecyclerView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"                                        
    Android:layoutAnimation="@anim/layout_animation_fall_down"
    />

merci pour l'excellent article ici: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213

11
Pavel Biryukov

Voici un bon point de départ: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/Java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.Animation

Vous n'avez même pas besoin de la bibliothèque complète, cette classe suffit . Ensuite, si vous implémentez simplement votre classe Adapter, donnez un animateur comme celui-ci:

@Override
protected Animator[] getAnimators(View view) {
    return new Animator[]{
            ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
    };
}

@Override
public long getItemId(final int position) {
    return getWrappedAdapter().getItemId(position);
}

vous verrez les éléments apparaître par le bas à mesure qu'ils défilent, ce qui évite également le problème du défilement rapide.

8

L'animation d'éléments dans la vue recyclée lorsqu'ils sont liés dans l'adaptateur peut ne pas être la meilleure idée, car cela peut entraîner une animation des éléments de la vue recyclée à des vitesses différentes. Dans mon cas, l'élément situé à la fin de la liste de recyclage se positionne plus rapidement que celui situé en haut de la liste, car ceux-ci doivent encore être déplacés, de sorte que leur apparence est désordonnée.

Le code original que j'ai utilisé pour animer chaque élément dans la vue de recyclage peut être trouvé ici:

http://frogermcs.github.io/Instagram-with-Material-Design-conign-is-getting-real/

Mais je vais copier et coller le code au cas où le lien se briserait.

STEP 1: Définissez ceci dans votre méthode onCreate afin de vous assurer que l'animation ne s'exécute qu'une fois:

if (savedInstanceState == null) {
    pendingIntroAnimation = true;
}

STEP 2: Vous aurez besoin de mettre ce code dans la méthode par laquelle vous voulez démarrer l'animation:

if (pendingIntroAnimation) {
    pendingIntroAnimation = false;
    startIntroAnimation();
}

Dans le lien, l'auteur est en train d'animer les icônes de la barre d'outils. Il l'a donc inséré dans cette méthode:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    inboxMenuItem = menu.findItem(R.id.action_inbox);
    inboxMenuItem.setActionView(R.layout.menu_item_view);
    if (pendingIntroAnimation) {
        pendingIntroAnimation = false;
        startIntroAnimation();
    }
    return true;
}

STEP 3: Maintenant écrivez la logique pour startIntroAnimation ():

private static final int ANIM_DURATION_TOOLBAR = 300;

private void startIntroAnimation() {
    btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));

    int actionbarSize = Utils.dpToPx(56);
    toolbar.setTranslationY(-actionbarSize);
    ivLogo.setTranslationY(-actionbarSize);
    inboxMenuItem.getActionView().setTranslationY(-actionbarSize);

    toolbar.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(300);
    ivLogo.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(400);
    inboxMenuItem.getActionView().animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(500)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    startContentAnimation();
                }
            })
            .start();
}

Mon alternative préférée:

Je préférerais animer l'ensemble de la vue de recyclage plutôt que les éléments à l'intérieur de la vue de recyclage.

Les étapes 1 et 2 restent les mêmes.

Dans STEP 3, dès que votre appel API reviendra avec vos données, je commencerai l'animation.

private void startIntroAnimation() {
    recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
    recyclerview.setAlpha(0f);
    recyclerview.animate()
            .translationY(0)
            .setDuration(400)
            .alpha(1f)
            .setInterpolator(new AccelerateDecelerateInterpolator())
            .start();
}

Cela animerait toute votre vue de recyclage afin qu'elle vienne du bas de l'écran.

3
Simon

Ajouter cette ligne à RecyclerView.xml

Android:animateLayoutChanges="true"
0
Avinash Verma

Étend juste votre adaptateur comme ci-dessous 

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

Et ajouter une super méthode à onBindViewHolder

@Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        super.onBindViewHolder(holder, position);

C'est un moyen automatisé de créer un adaptateur animé comme "Basheer AL-MOMANI"

import Android.support.v7.widget.RecyclerView;
import Android.view.View;
import Android.view.ViewGroup;
import Android.view.animation.Animation;
import Android.view.animation.ScaleAnimation;

import Java.util.Random;

/**
 * Created by eliaszkubala on 24.02.2017.
 */
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {


    @Override
    public T onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(T holder, int position) {
        setAnimation(holder.itemView, position);
    }

    @Override
    public int getItemCount() {
        return 0;
    }

    protected int mLastPosition = -1;

    protected void setAnimation(View viewToAnimate, int position) {
        if (position > mLastPosition) {
            ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
            viewToAnimate.startAnimation(anim);
            mLastPosition = position;
        }
    }

}
0
EliaszKubala

Sans avoir de codage.

Visitez Gist Link

  <?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:animation="@anim/item_animation_fall_down"
    Android:animationOrder="normal"
    Android:delay="15%" />

<translate
    Android:fromYDelta="-20%"
    Android:toYDelta="0"
    Android:interpolator="@Android:anim/decelerate_interpolator"
    />

<alpha
    Android:fromAlpha="0"
    Android:toAlpha="1"
    Android:interpolator="@Android:anim/decelerate_interpolator"
    />

<scale
    Android:fromXScale="105%"
    Android:fromYScale="105%"
    Android:toXScale="100%"
    Android:toYScale="100%"
    Android:pivotX="50%"
    Android:pivotY="50%"
    Android:interpolator="@Android:anim/decelerate_interpolator"
    />

Utilisez dans les mises en page et recylcerview comme:

<Android.support.v7.widget.RecyclerView
            Android:id="@+id/recycler_view"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layoutAnimation="@anim/layout_animation"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
0
Na Ran