How to animate RecyclerView items when they appear
Asked Answered
U

11

303

How can I animate the RecyclerView Items when there are appearing?

The default item animator only animates when a data is added or removed after the recycler data has been set.

How can this be achieved?

Unbind answered 3/11, 2014 at 23:10 Comment(0)
N
131

Made Simple with XML only

Visit Gist Link

res/anim/layout_animation.xml

<?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%" />

res/anim/item_animation_fall_down.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <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"
        />

</set>

Use in layouts and recylcerview like:

<androidx.recyclerview.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" />
Nisbet answered 13/2, 2019 at 11:8 Comment(10)
@ArnoldBrown By changing the animation file. Refer: #5152091Nisbet
This is by far the most practical answer.Silicle
how to make this work every time the list is opened, because now it only do it for the first time.Geibel
You have to call recyclerView.scheduleLayoutAnimation() after data set changed, if not, the animation would not work.Reconstructionism
Should this work for when items are recycled and come back into view? I attempted this solution and it works great for the initial animation when you can first see the layout. After scrolling, items do not have the animation when coming back to view.Ladin
@Jasonp to do that you will need to do the animation in your onBindViewHolder, so for each time an item is binded, you will get the animation on scrollingKeys
@Nisbet now I'm trying to reproduce your solution but I got some outside elements that weren't animated: imgur.com/a/F1pGjbL What's the problem?Kaliningrad
you can follow these guidelines regarding duration for better experience: material.io/design/motion/speed.html#easingAgonic
this answer doesnt work when you notifyItemInserted on the adapter, this just animates the parent view, not the items insideAgonic
How can I not apply this animation only for the first item? I want to make the list come out of the first item and drop down one from the next one.Kainite
W
346

EDIT :

According to the ItemAnimator documentation :

This class defines the animations that take place on items as changes are made to the adapter.

So unless you add your items one by one to your RecyclerView and refresh the view at each iteration, I don't think ItemAnimator is the solution to your need.

Here is how you can animate the RecyclerView items when they appear using a 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;
        }
    }
}

And your custom_item_layout would look like this :

<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>

For more information about CustomAdapters and RecyclerView, refer to this training on the official documentation.

Problems on fast scroll

Using this method could cause problems with fast scrolling. The view could be reused while the animation is been happening. In order to avoid that is recommendable to clear the animation when is detached.

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

On CustomViewHolder:

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

Old answer :

Give a look at Gabriele Mariotti's repo, I'm pretty sure you'll find what you need. He provides simple ItemAnimators for the RecyclerView, such as SlideInItemAnimator or SlideScaleItemAnimator.

Wellappointed answered 5/11, 2014 at 1:33 Comment(31)
I've seen that, but that's for adding and removing items after they've appeared. I need to start animation right before they appeard. Thanks anyway Mathieu.Unbind
As far as I know, you need to use a CustomAdapter then.Wellappointed
@Wellappointed but can you imagine this sense: while i'm using this CustomAdapter, when I scroll from position 0 to 100, these items all play this animation, but when I scroll back, position 0's item will play again. Is there any way to avoid this happen?Furlana
@KevinLiu Not sure to understand what you ask. Are you saying the first item is animated when you scroll back to the top?Wellappointed
@Wellappointed yes, I don't want the item which has been played its animation play again. I thought maybe I need a Map to store the state of every item's animation? like "done" or "not yet".Furlana
@KevinLiu I don't get it, they're not supposed to be animated again; that's why we keep lastPosition updated, to know the index of the last animated item. If you already use it, can you be more explicit about your problem?Wellappointed
I was wondering if you've experienced and maybe solved the "stuck" effect with these animations in RecyclerView? I used similiar code for the ListView and my items were animated without a problem no matter how fast I scrolled, but with RecyclerView if I scroll fast some items sometimes get stuck on the screen on top of the other items and they don't hide entirely - it's like the animation was stopped before it finished. I actually tried to comment out the part of the code which populates the fields (trying to speed up the execution of onBindViewHolder method) so I only left the code for animatArchiepiscopate
@Wellappointed Thanks for this amaze animation.It looks good for slow scroll,but on fast scroll recyclerview items overlapped.Did you find this issse?Any suggestion to overcome this issue.Itch
@GiruBhai, user2489759 : As a matter of fact, I did... even though I'm not even using this animation. I believe there's something wrong with this adapter but can't figure out what. Feel free to suggest anything, I'll look into it myself too.Wellappointed
@GiruBhai, user2489759 : After digging into it, I discovered that it's linked to the fact that I'm using the StaggeredGridLayoutManager. When using either LinearLayoutManager or GridLayoutManager, it appears I have no problem at all. Have you noticed anything similar?Wellappointed
@Wellappointed in my case recycleview have LinearLayoutManager and having problem during fast scroll.Itch
@Wellappointed works perfect for me, i used LinearLayoutManager, Do you anyway to set animation for the items which go out of site. I mean this code animates items when then appear. So i need to know is there anyway we can animate the items that are about to disappear.Tonsil
@GiruBhai override onViewDetachedFromWindow and call clearAnimation on the view. The problem is that there are animations running when RecyclerView is trying to reuse the view.Parterre
@AshokVarma the view that is about to disappear is handed to onViewDetachedFromWindowParterre
Animate items in RecyclerView from outside (from adapter for example) is really bad practice because RecyclerView easily can rebind your holder while animating. This lead to undefined state for those holders and weird errors. I don't know hot to animate views when they appear, but I bet you, don't animate those views outside RecyclerView, this will save you a lot nerve.Tennison
@Parterre thx for this solution, I was wondering about this issue and didn't have any idea how to solve this problem :-)Exchangeable
how can i make the animation work one after the other, currently this animates all the items at once. i want to animate one item then the next and so on.Graecoroman
notifyDataSetChanged has a pattern of abruptly updating the recyclerview with new data/removing old data. Does this setAnimation() permit animation even when (or especially when) that method is called?Freezedrying
@KevinLiu i am having the same problem... how did u resolve that??Zama
@AmanVerma just like Mathieu Maree said to me. see above about our discuss.Furlana
doesn't super.onViewDetachedFromWindow(holder); need to be called first? I'm not sure where mRootLayout is initialized so I used ((MyViewHolder)holder).itemView.clearAnimation();Beira
The code above tt's clear to me, however, when I set the adapter to my recycler, all the visible views animates together causing an ugly effect. How can I animate (at first show) all the items one by one? Should I add the items accordingly?Rugging
I use grid layout manager with 2 span, but the animation occur only on the right side items. Anyone has the same problem and has the solution?Predilection
How to give animation load "from bottom to top"?Latvian
@Latvian Simply replace android.R.anim.slide_in_left by R.anim.slide_bottom_to_top and create a new file with your bottom-to-top translation animation in your res/anim folderWellappointed
where to put these functions ? onViewDetachedFromWindow and clearAnimation ?? in custom ViewHolder class or adapter ??Denominational
@Gavriel: clearAnimation() is a method in your RecyclerView.ViewHolder class. The constructor accepts itemView as your root layout. You can use something like itemView.clearAnimation()Muck
@Wellappointed great job, I followed your code, but animation runs only on first load not on scroll down, if I remove if (position > lastPosition) then animation runs on scroll up/down for all items (on every scroll), any idea what is the problem?Reproval
@Wellappointed seems like the problem is the XML, the parent of my recyclerview have layout_weight, if I move the recyclerview one level up (with no layout_weight on parent), everything works fine, any idea?Reproval
@Override public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder) { ((CustomViewHolder)holder).clearAnimation(); } doesnot resoled method in my projectRidgepole
I understand itRidgepole
N
131

Made Simple with XML only

Visit Gist Link

res/anim/layout_animation.xml

<?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%" />

res/anim/item_animation_fall_down.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <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"
        />

</set>

Use in layouts and recylcerview like:

<androidx.recyclerview.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" />
Nisbet answered 13/2, 2019 at 11:8 Comment(10)
@ArnoldBrown By changing the animation file. Refer: #5152091Nisbet
This is by far the most practical answer.Silicle
how to make this work every time the list is opened, because now it only do it for the first time.Geibel
You have to call recyclerView.scheduleLayoutAnimation() after data set changed, if not, the animation would not work.Reconstructionism
Should this work for when items are recycled and come back into view? I attempted this solution and it works great for the initial animation when you can first see the layout. After scrolling, items do not have the animation when coming back to view.Ladin
@Jasonp to do that you will need to do the animation in your onBindViewHolder, so for each time an item is binded, you will get the animation on scrollingKeys
@Nisbet now I'm trying to reproduce your solution but I got some outside elements that weren't animated: imgur.com/a/F1pGjbL What's the problem?Kaliningrad
you can follow these guidelines regarding duration for better experience: material.io/design/motion/speed.html#easingAgonic
this answer doesnt work when you notifyItemInserted on the adapter, this just animates the parent view, not the items insideAgonic
How can I not apply this animation only for the first item? I want to make the list come out of the first item and drop down one from the next one.Kainite
J
74

I animated fading in of Recyclerview items when they first appear as shown in the code below. Perhaps this will be of use to someone.

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);
}

You can also replace setFadeAnimation() with the following setScaleAnimation() to animate appearance of items by scaling them from a 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);
}

The code above has some warts in so far as when you scroll the RecyclerView items always fade or scale. If you wish you can add code to just allow the animation to happen when the fragment or activity containing the RecyclerView is first created (e.g. get the system time on creation and only allow animation for the first FADE_DURATION milliseconds).

Jeddy answered 27/1, 2016 at 7:53 Comment(3)
I did small modification for your answer to make animation work on scroll down only, check my answerAzerbaijani
this spoils the layout ( over-written list items, some items having wrong text color ) on fast scrolling up and downDenominational
This is not the proper or recommended way to animate recyclerview items. You must be using ItemAnimator classIgnorant
A
33

I created animation from pbm's answer with little modification to make the aninmation run only once

in the other word the 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;
    }
}

and in onBindViewHolder call the function

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

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

// call Animation function
setAnimation(holder.itemView, position);            
}
Azerbaijani answered 11/4, 2016 at 10:9 Comment(2)
What is lastPosition here?Is it arrayList.size() - 1Coronograph
lastPosition represents the number of rendered views, so it the beginning its value -1, every time a new view is being rendered we start an animation and increase the positionAzerbaijani
D
17

You can add a android:layoutAnimation="@anim/rv_item_animation" attribute to RecyclerView like this:

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

thanks for the excellent article here: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213

Duaneduarchy answered 20/2, 2018 at 9:5 Comment(2)
This worked for me when a recyclerview first loads, but not when adding new items. (Though I guess that was the question so good job)Orsola
This is the perfect answerGyroplane
J
10

A good place to start is this: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

You don't even need the full library, that class is enough. Then if you just implement your Adapter class giving an animator like this:

@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);
}

you'll see items appearing from the bottom as they scroll, also avoiding the problem with the fast scroll.

Jowett answered 16/11, 2015 at 19:32 Comment(0)
U
6

Animating items in the recyclerview when they are binded in the adapter might not be the best idea as that can cause the items in the recyclerview to animate at different speeds. In my case, the item at the end of the recyclerview animate to their position quicker then the ones at the top as the ones at the top have further to travel so it made it look untidy.

The original code that I used to animate each item into the recyclerview can be found here:

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

But I will copy and paste the code in case the link breaks.

STEP 1: Set this inside your onCreate method so that you ensure the animation only run once:

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

STEP 2: You will need to put this code into the method where you want to start the animation:

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

In the link, the writer is animating the toolbar icons, so he put it inside this method:

@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: Now write the logic for the 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();
}

My preferred alternative:

I would rather animate the whole recyclerview instead of the items inside the recyclerview.

STEP 1 and 2 remains the same.

In STEP 3, as soon as your API call returns with your data, I would start the animation.

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

This would animate your entire recyclerview so that it flys in from the bottom of the screen.

Unregenerate answered 23/1, 2016 at 20:42 Comment(4)
you are talking about animating the whole recyclerviewDifferent
Yes - I'm. You should read the first paragraph on why I think animating the whole recyclerview is a better idea than each item. You can try animating each item but it will not look good.Unregenerate
What is latestPostRecyclerview?Housebreaking
Did u read the first paragraph of my answer where I had stated the reason why animating item views are not such a great idea? Also I would suggest trying out the accepted solution and then you realize the problem, come back and try this solution.Unregenerate
A
6

Create this method into your recyclerview Adapter

private void setZoomInAnimation(View view) {
        Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file 
        view.startAnimation(zoomIn);
    }

And finally add this line of code in onBindViewHolder

setZoomInAnimation(holder.itemView);

Abysm answered 31/7, 2017 at 11:34 Comment(0)
N
4

In 2019, I would suggest putting all the item animations into the ItemAnimator.

Let's start with declaring the animator in the recycler-view:

with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}

Declare the custom animator then,

class CustomAnimator() : DefaultItemAnimator() {

     override fun animateAppearance(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo?,
       postInfo: ItemHolderInfo): Boolean{} // declare  what happens when a item appears on the recycler view

     override fun animatePersistence(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo,
       postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change

}

Similar to the ones above there is one for disappearance animateDisappearance, for add animateAdd, for change animateChange and move animateMove.

One important point would be to call the correct animation-dispatchers inside them.

Neustria answered 2/12, 2019 at 8:52 Comment(2)
Could you provide an example of a custom appearance animation using this override function? I can't find an example and i'm not sure if i just have to specify the animation in the function.Stingy
gist.github.com/tadfisher/120d03f8380bfa8a16bf I found this online on a quick search, this gives an idea of how the overloading worksNeustria
R
2

I think, is better to use it like this: (in RecyclerView adapter override just a one method)

override fun onViewAttachedToWindow(holder: ViewHolder) {
    super.onViewAttachedToWindow(holder)

    setBindAnimation(holder)
}

If You want every attach animation there in RV.

Roundhouse answered 21/1, 2020 at 18:53 Comment(0)
C
1

Just extends your Adapter like below

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

And add super method to onBindViewHolder

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

It's automate way to create animated adapter like "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;
        }
    }

}
Chipper answered 24/2, 2017 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.