ChatGPT解决这个技术问题 Extra ChatGPT

How to animate RecyclerView items when they appear

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. I'm new developing applications and don't have any clue where to start.

Any ideas how to achieve this?


d
dgngulcan

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.


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.
As far as I know, you need to use a CustomAdapter then.
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 animat
@MathieuMaree 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.
@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.
C
Cyb3rKo

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

@ArnoldBrown By changing the animation file. Refer: stackoverflow.com/questions/5151591/…
This is by far the most practical answer.
how to make this work every time the list is opened, because now it only do it for the first time.
You have to call recyclerView.scheduleLayoutAnimation() after data set changed, if not, the animation would not work.
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.
A
Arpit J.

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


I did small modification for your answer to make animation work on scroll down only, check my answer
this spoils the layout ( over-written list items, some items having wrong text color ) on fast scrolling up and down
This is not the proper or recommended way to animate recyclerview items. You must be using ItemAnimator class
C
Community

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

What is lastPosition here?Is it arrayList.size() - 1
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 position
A
AskNilesh

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


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)
This is the perfect answer
P
Patrick Favre

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.


S
Simon

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.


you are talking about animating the whole recyclerview
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.
What is latestPostRecyclerview?
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.
M
Md.Tarikul Islam

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


D
Dinesh

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.


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.
gist.github.com/tadfisher/120d03f8380bfa8a16bf I found this online on a quick search, this gives an idea of how the overloading works
E
Eliasz Kubala

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

}

M
Milan Jurkulak

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.