Android RecyclerView Layout Animations

Filed Under: Android

In this tutorial, we’ll be discussing and implementing RecyclerView Layout Animations in our Android Application.

Android RecyclerView Layout Animations

There are many ways to Animate rows in a RecyclerView.
Two commonly tried and tested ways are :

  • Using ItemAnimators.Read this tutorial.
  • Setting animation on each row in the onBindViewHolder in the Adapter class

There’s another lesser-known but more efficient way of animating a RecyclerView using Layout Animations.

We can directly pass the animation resource asset in the XML on the attribute android:layoutAnimation.

The anim resource passed must contain <layoutAnimation> as the root tag.
layoutAnimation is valid on all other layouts as well besides RecyclerView.

Let’s start by defining some basic Animations in the res | anim folder in our Android Studio Project.

down_to_up.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromYDelta="50%p"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toYDelta="0" />

    <alpha
        android:fromAlpha="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />

</set>

up_to_down.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

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

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

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

</set>

left_to_right.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromXDelta="-100%p"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="0" />

    <alpha
        android:fromAlpha="0.5"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />

</set>

right_to_left.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromXDelta="100%p"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="0" />

    <alpha
        android:fromAlpha="0.5"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />

</set>

Now let’s create the layoutAnimation for each of these animation sets.

layout_animation_down_to_up.xml


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

layout_animation_up_to_down.xml


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

layout_animation_left_to_right.xml


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

layout_animation_right_to_left.xml


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

Setting Layout Animation in XML and Programmatically

We can set the Layout Animation on RecyclerView in XML in the following way:


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

Programmatically:


int resId = R.anim.layout_animation_right_to_left;
LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(context, resId);
recyclerView.setLayoutAnimation(animation);

In order to re-run the animation or in case the data set of the RecyclerView has changed the following code is used:


final LayoutAnimationController controller =
            AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_right_to_left);

    recyclerView.setLayoutAnimation(controller);
    recyclerView.getAdapter().notifyDataSetChanged();
    recyclerView.scheduleLayoutAnimation();

Project Structure

android recyclerview layout animation output

Code

The code for the activity_main.xml layout is given below:


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layoutAnimation="@anim/layout_animation_up_to_down"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:src="@android:drawable/ic_media_next"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</android.support.constraint.ConstraintLayout>

FloatingActionButton is used to toggle through the different layout animations.

The layout for the rows of RecyclerView is defined in item_row.xml as shown below:


<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardElevation="8dp"
    app:cardUseCompatPadding="true">

    <TextView
        android:id="@+id/tvItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="Item X" />

</android.support.v7.widget.CardView>

The code for the RecyclerViewAdapter.java class is given below.


package com.journaldev.androidrecyclerviewlayoutanimation;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> {


    List<String> itemList;


    public RecyclerViewAdapter(List<String> itemList) {
        this.itemList = itemList;
    }


    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_row, viewGroup, false);
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder myViewHolder, int position) {
        myViewHolder.tvItem.setText(itemList.get(position));

    }

    @Override
    public int getItemCount() {
        return itemList == null ? 0 : itemList.size();
    }

    public class ItemViewHolder extends RecyclerView.ViewHolder {

        TextView tvItem;

        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);

            tvItem = itemView.findViewById(R.id.tvItem);
        }
    }
}

The code for the MainActivity.java class is given below:


package com.journaldev.androidrecyclerviewlayoutanimation;

import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.GridLayoutAnimationController;
import android.view.animation.LayoutAnimationController;
import android.widget.Spinner;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    FloatingActionButton fab;
    RecyclerView recyclerView;
    RecyclerViewAdapter recyclerViewAdapter;
    ArrayList<String> arrayList = new ArrayList<>();


    int[] animationList = {R.anim.layout_animation_up_to_down, R.anim.layout_animation_right_to_left, R.anim.layout_animation_down_to_up, R.anim.layout_animation_left_to_right};

    int i = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fab = findViewById(R.id.fab);
        recyclerView = findViewById(R.id.recyclerView);
        populateData();
        initAdapter();

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (i < animationList.length - 1) {
                    i++;
                } else {
                    i = 0;
                }
                runAnimationAgain();

            }
        });


    }

    private void populateData() {

        for (int i = 0; i < 12; i++) {
            arrayList.add("Item " + i);
        }
    }

    private void initAdapter() {
        recyclerViewAdapter = new RecyclerViewAdapter(arrayList);
        recyclerView.setAdapter(recyclerViewAdapter);
    }

    private void runAnimationAgain() {

        final LayoutAnimationController controller =
                AnimationUtils.loadLayoutAnimation(this, animationList[i]);

        recyclerView.setLayoutAnimation(controller);
        recyclerViewAdapter.notifyDataSetChanged();
        recyclerView.scheduleLayoutAnimation();

    }
}

runAnimationAgain method is used to loop through each animation and run them on the RecyclerView again.

The output of the above application in action is given below:

android recyclerview layout animation output 1

Now if we apply a GridLayoutManager as the layout manager we’ll get the following output:


recyclerView.setLayoutManager(new GridLayoutManager(this,2,GridLayoutManager.VERTICAL,false));

android recyclerview layout animation output grid

The above layout animation is not grid layout animation. It’s animating each row at a time.
This is incorrect. The RecyclerView LayoutAnimation defaults for Lists only.
In order to do Grid Layout Animation, we need to customize the recycler view.
We’ll do that in the next tutorial.

That brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Harsh Barnwal says:

    How can we slow up the animation speed?

  2. Pradeepkumar Reddy says:

    Will this animation work if i’m not using notifyDataSetChanged() ?
    I’m using diffutil for recyclerview. I tried it, but this is actually not working.

  3. Pradeepkumar Reddy says:

    Will this animation work if i’m not using notifyDataSetChanged() ?

  4. Suleyman says:

    Thank you man.You very helpful.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages