22

I have a recycled view which is populated with CardView layouts. I've noticed that in many apps, these items have a nice animated entrance rather than abruptly appearing as they currently do in my application. An example of what I'm trying to achieve is shown below: enter image description here

How would I use my swipeRefreshListener/adapter to trigger this animation? How would I implement this through my adapter?

Note: I need this animation to also be triggered when the RecyclerView is first populated with the CardView items, and the data they contain.

My RecyclerView's adapter is shown below:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

private String[] mDataset;
private String[] mClassDataset;
private int lastPosition = -1;
private Context context;

View view;


public static class ViewHolder extends RecyclerView.ViewHolder {

    public TextView mTextView;
    public TextView mClassDataView;
    CardView container;


    public ViewHolder(View itemView) {
        super(itemView);
        mTextView = (TextView)itemView.findViewById(R.id.grade);
        mClassDataView = (TextView)itemView.findViewById(R.id.class_name);
        container = (CardView)itemView.findViewById(R.id.card_view);
    }
}

public RecyclerAdapter(String[] myDataset, String[] classData, Context context) {
    mDataset = myDataset;
    mClassDataset = classData;
    this.context = context;

}

@Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_item, parent, false);

    ViewHolder vh = new ViewHolder(v);

    view = v;

    return vh;
}

@Override
public int getItemCount() {
    return mClassDataset.length;
}

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


    if (mDataset.length < mClassDataset.length) {

        holder.mTextView.setText("N/A");
        holder.mClassDataView.setText(mClassDataset[position]);

        if (mClassDataset[position].equals("N/A")) {

        }

    } else {
        holder.mTextView.setText(mDataset[position]);
        holder.mClassDataView.setText(mClassDataset[position]);
    }

    for (int i = 0; i < getItemCount(); i++) {

        animate(view, i);

    }
}

private void animate(final View view, final int position){

        Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
        view.startAnimation(animation);
        lastPosition = position;

    }

}

The animation is currently working, however the thing is the items are being animated all at once. I tried iterating through the items by creating a custom getChild() method, but that didn't work. Somehow I need to access/animate each child individually and possibly set a delay between each animation. However, I'm not sure how exactly I can accomplish this. I know I can use a Handler to set delay, but accessing each child is a problem.

mlz7
  • 2,067
  • 3
  • 27
  • 51

3 Answers3

44

Use this custom class of RecyclerView, it behaves like the app that you posted its screenshot (Reddit Relay), it prevents scrolling while animating first items. You may want to custom the animation as you want.

package net.leolink.android.testrecyclerviewentranceanimation;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * @author Leo on 2015/09/03
 */
public class MyRecyclerView extends RecyclerView {
    private boolean mScrollable;

    public MyRecyclerView(Context context) {
        this(context, null);
    }

    public MyRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScrollable = false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return !mScrollable || super.dispatchTouchEvent(ev);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        for (int i = 0; i < getChildCount(); i++) {
            animate(getChildAt(i), i);

            if (i == getChildCount() - 1) {
                getHandler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mScrollable = true;
                    }
                }, i * 100);
            }
        }
    }

    private void animate(View view, final int pos) {
        view.animate().cancel();
        view.setTranslationY(100);
        view.setAlpha(0);
        view.animate().alpha(1.0f).translationY(0).setDuration(300).setStartDelay(pos * 100);
    }
}


DEMO

Demo

Leo
  • 1,433
  • 23
  • 40
  • would I change my layout so that it has the custom recyclerview instead of a regular recyclerview? also, would I cast to my custom recyclerview in my activity? – mlz7 Sep 05 '15 at 00:57
  • Yes for both questions :) – Leo Sep 05 '15 at 05:34
  • this worked great thanks. Just out of curiosity, why is it necessary to include all of the constructers you added? (I tried to create a custom RecyclerView class with a single constructor and without handling the scrollable variable but things didn't work) – mlz7 Sep 06 '15 at 00:29
  • No, you don't have to include all the constructors I wrote, only the second one will be called if you use it in XML (it didn't work for you because I used 'this' instead of 'super' in the first and second constructors). And you have to have scrolling disabled here otherwise when you user scrolls down while the animation is in progress, it would look weird. By the way if my answer works for you, please accept and award me the bounty :) – Leo Sep 06 '15 at 01:13
  • hehe, actually it took me a morning to figure out the perfect answer because I was curious too, so I guess I deserve it :p – Leo Sep 06 '15 at 01:25
  • yup don't worry you're definitely getting the bounty. Last question though lol: why is it that only the second constructor can work independently (e.g. when i use only the first/third it does not work)? – mlz7 Sep 06 '15 at 01:54
  • because when you use this custom view in xml, stuff like 'layout_width="match_parent"' will be bundled in to 'attrs', theb Android pass it via the second one or third one because obviously the first one doesn't take Attributes. – Leo Sep 06 '15 at 01:58
  • I see. I appreciate all of your help! – mlz7 Sep 06 '15 at 02:00
  • 1
    @Leo I have a small issue: if I wrap the recyclerview in a swiprefreshlayout, it triggers the animation as soon as I "pull" the swiperefreh. Is there a way to prevent that from happening? – just_deko Oct 17 '16 at 15:32
  • How to add remove item animation .? – akhil batlawala Dec 10 '16 at 06:12
  • Sir(@Leo) it is very helpful but since I am still learning recycler view will you please mention how and where do I add this to my ViewHolder? –  Sep 14 '17 at 06:11
  • when scroll-down items below items not showing with animation only show animation first displayed item i want animation for all items how to do this? – full Error Jul 18 '19 at 07:29
4

First define the animation:

<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0"
    android:duration="300"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
    android:duration="300" />
</set>

Then set it in your adapter:

    @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.container, 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.push_left_in);
        viewToAnimate.startAnimation(animation);
        lastPosition = position;
    }
}

Referenced from: RecyclerView : How to create insert animation effect?

Community
  • 1
  • 1
LonelyIdiot
  • 789
  • 2
  • 6
  • 16
3

For the RecyclerView you can use ItemAnimators by setting them with this method:

RecyclerView.setItemAnimator(RecyclerView.ItemAnimator animator)

There are a few libraries out there that implement this ItemAnimator, for example this one: https://github.com/wasabeef/recyclerview-animators

Thorben
  • 1,019
  • 7
  • 20
  • yeah I looked at that lib but it didn't have what I was looking for – mlz7 Aug 27 '15 at 16:19
  • @Steve That's how you would do it. if you can't find exactly what you're looking for then, you'd have to write your own. – tachyonflux Sep 03 '15 at 03:23
  • @karaokyo that's not how you handle animations while populating recyclerviews – mlz7 Sep 03 '15 at 03:53
  • @Steve what are they used for then? – tachyonflux Sep 03 '15 at 04:14
  • @karaokyo to accomplish what I am trying to do using itemAnimators, you would need to add RecyclerView items one by one, set animator, and then refresh view (repeated for all populating views); all of which is unnecessarily tedious when it should be possible to accomplish this within the adapter itself – mlz7 Sep 03 '15 at 04:19