In this tutorial, we’ll learn how to create animation for a RecyclerView in which elements are displayed in the form of a Grid.
We’ve already discussed RecyclerView Layout Animations earlier using a List.
RecyclerView Grid Animation
In the previous tutorial, we created List based Layout Animations. At the end of the tutorial, we applied them to a Grid Layout and saw that the animation is still list based on the grid instead of row and column based.
In order to show Grid-based animation we need to use <gridlayoutanimation
tag.
But this won’t work on a normal RecyclerView. It will crash since by default a RecyclerView doesn’t know anything about how the elements are placed by the Layout Manager and assumes a list based placement.
Hence we need to create a custom RecyclerView first, overriding the important methods.
Let’s get started with our application.
Project Structure
Code
The code for the CustomGridRecyclerView.java
class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.journaldev.androidrecyclerviewgridlayoutanimations; import android.content.Context; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.GridLayoutAnimationController; public class CustomGridRecyclerView extends RecyclerView { public CustomGridRecyclerView(Context context) { super(context); } public CustomGridRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomGridRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setLayoutManager(LayoutManager layout) { if (layout instanceof GridLayoutManager) { super.setLayoutManager(layout); } else { throw new ClassCastException("This recyclerview should use grid layout manager as the layout manager"); } } @Override protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) { if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager) { GridLayoutAnimationController.AnimationParameters animationParams = (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters; if (animationParams == null) { animationParams = new GridLayoutAnimationController.AnimationParameters(); params.layoutAnimationParameters = animationParams; } int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount(); animationParams.count = count; animationParams.index = index; animationParams.columnsCount = columns; animationParams.rowsCount = count / columns; final int invertedIndex = count - 1 - index; animationParams.column = columns - 1 - (invertedIndex % columns); animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns; } else { super.attachLayoutAnimationParameters(child, params, index, count); } } } |
In the above code, we’ve explicitly set the RecyclerView to use grids row and columns for animating.
The code for the activity_main.xml is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.journaldev.androidrecyclerviewgridlayoutanimations.CustomGridRecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" 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> |
The code for the MainActivity.java is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package com.journaldev.androidrecyclerviewgridlayoutanimations; 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.view.View; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; import android.widget.GridLayout; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { CustomGridRecyclerView recyclerView; RecyclerViewAdapter recyclerViewAdapter; FloatingActionButton fab; ArrayList<String> arrayList = new ArrayList<>(); @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) { runAnimationAgain(); } }); } private void populateData() { for (int i = 0; i < 20; i++) { arrayList.add("Item " + i); } } private void initAdapter() { recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); recyclerViewAdapter = new RecyclerViewAdapter(arrayList); recyclerView.setAdapter(recyclerViewAdapter); } private void runAnimationAgain() { final LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.gridlayout_animation_from_bottom); recyclerView.setLayoutAnimation(controller); recyclerViewAdapter.notifyDataSetChanged(); recyclerView.scheduleLayoutAnimation(); } } |
The animation set is defined in the anim
folder in the file down_to_up.xml
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="https://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> |
The grid layout animation is defined in the file: gridlayout_animation_from_bottom.xml
.
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <gridLayoutAnimation xmlns:android="https://schemas.android.com/apk/res/android" android:animation="@anim/down_to_up" android:animationOrder="normal" android:columnDelay="15%" android:direction="top_to_bottom|left_to_right" android:rowDelay="15%" android:startOffset="700" /> |
The row and column delay attributes are used to define the delay in animation in each row and column respectively after the previous row got animated.
The code for the RecyclerViewAdapter.java class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.journaldev.androidrecyclerviewgridlayoutanimations; 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 output of the above application in action is given below:
That brings an end to this tutorial. You can download the complete project from the link below: