RecyclerView in Android
Android RecyclerView was introduced in Android 5.0 Lollipop. It is much faster and memory efficient than Android ListView.
You can download sample application source code for this tutorial from here.
A RecyclerView can cache previously used view for a specific adapter position for later reuse to display same type of data again later. This improves the performance of the RecyclerView drastically by skipping initial layout inflation or construction.
We would have to learn about the following classes in order to use RecyclerView in our apps:
1. RecyclerView.Adapter: It provides access to data sets items, creates views for items and binds items info to item view. It is a placeholder for ListView Adapter classes. Just like ListView adapter, it provides views to be fed to the RecyclerView. In onCreateViewHolder(), we inflate xml view resources and wrap it inside a RecyclerView.ViewHolder instances and return it.
[sourcecode language=”java” wraplines=”false” collapse=”false”]
@Override
public ListItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_view, parent, false);
return new ListItemViewHolder(itemView);
}
[/sourcecode]
In onBindViewHolder() we put the data in views. We access the views from ViewHolder supplied here:
[sourcecode language=”java” wraplines=”false” collapse=”false”]
@Override
public void onBindViewHolder(ListItemViewHolder holder, int position) {
DataModel model = mDataModels.get(position);
holder.title.setText(model.getTitle());
}
[/sourcecode]
2. RecyclerView.ViewHolder: ViewHolder class wraps the view instances inside it from where we can get child views directly. In this way we don’t need to inflate the view over and again every time we scroll the recycler view. It saves memory and make the scrolling fast and smooth. Here is the implementation of ViewHolder class which I subclassed:
[sourcecode language=”java” wraplines=”false” collapse=”false”]
public class ListItemViewHolder extends ViewHolder {
ImageView imgView;
TextView title;
public ListItemViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.textview_title);
imgView = (ImageView) itemView.findViewById(R.id.imageview_icon);
}
}
[/sourcecode]
3. RecyclerView.LayoutManager: It is responsible for measuring and positioning item views within a RecyclerView. It is responsible for recycling out of screen views during scrolling. RecyclerView.LayoutManager has three subclasses.
-
LinearLayoutManager: It provides similar functionality to a standard ListView. We can create a Vertical Scrolling List as well as Horizontal Scrolling List using this manager by specifying the scroll type in its constructor while creating its instance.
-
GridLayoutManager: Implementation of layout manager that lays out items in a grid. It provides only vertical scrolling.
-
StaggeredGridLayoutManager: Lays out all items in a staggered grid formation, supports horizontal and vertical layouts and is able to lay out items in reverse. It can layout irregular sized items in a vertical or horizontal scrolling grid view
4. RecyclerView.ItemAnimator: This class defines the animations that take place on items as changes are made to the adapter for example adding items, removing items and reordering items. Android provides a default item animator called DefaultItemAnimator. We set the animator to the RecyclerView by calling setItemAnimator (RecyclerView.ItemAnimator animator) method on RecyclerView object
5. RecyclerView.ItemDecoration: An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. No default ItemDecoration implementation is provided by the Android. Here is sample code for ItemDecoration implementation found at github.com.
[sourcecode language=”java” wraplines=”false” collapse=”false”]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() – parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() – parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
[/sourcecode]
Here is the the complete implementation of the RecyclerView.Adapter class in the sample project:
[sourcecode language=”java” wraplines=”false” collapse=”false”]
package com.demo.recyclerview;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* Created by noor on 05/05/15.
*/
public class PlaceholderFragment extends Fragment {
/*Number of columns in the grid view*/
private static final int NUM_OF_COLUMNS = 2;
/*Total number of items in the RecyclerView*/
private static final int NUM_OF_ITEMS = 100;
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerview);
RecyclerView.LayoutManager layoutManager = null;
String type = getArguments().getString(MainActivity.TYPE);
if( type.equals(MainActivity.TYPE_VERTICAL_LIST)){
/*LinearLayoutManager to show a vertical list view*/
layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
}else if(type.equals(MainActivity.TYPE_HORIZONTAL_LIST)){
/*LinearLayoutManager to show a horizontal list view*/
layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
}else if(type.equals(MainActivity.TYPE_GRID_VIEW)){
/*LinearLayoutManager to show a grid view. We can specify number of columns in the grid.*/
layoutManager = new GridLayoutManager(getActivity(), NUM_OF_COLUMNS);
}else if(type.equals(MainActivity.TYPE_HORIZONTAL_GRID_VIEW_STAGGERED)){
/*LinearLayoutManager to show a staggered grid view. We can specify number of columns in the grid.*/
//spanCount: If orientation is vertical, spanCount is number of columns. If orientation is horizontal, spanCount is number of rows.
//orientation: StaggeredGridLayoutManager.HORIZONTAL or StaggeredGridLayoutManager.HORIZONTAL
layoutManager = new StaggeredGridLayoutManager(3/*span count*/, StaggeredGridLayoutManager.HORIZONTAL/* orientation*/);
}else if(type.equals(MainActivity.TYPE_VERTICAL_GRID_VIEW_STAGGERED)){
/*LinearLayoutManager to show a staggered grid view. We can specify number of columns in the grid.*/
//spanCount: If orientation is vertical, spanCount is number of columns. If orientation is horizontal, spanCount is number of rows.
//orientation: StaggeredGridLayoutManager.HORIZONTAL or StaggeredGridLayoutManager.HORIZONTAL
layoutManager = new StaggeredGridLayoutManager(2/*span count*/, StaggeredGridLayoutManager.VERTICAL/* orientation*/);
}
recyclerView.setLayoutManager(layoutManager);
RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(getDataModelList(), type);
/*Third party ItemDecoration found from https://gist.github.com/alexfu/0f464fc3742f134ccd1e*/
RecyclerView.ItemDecoration verticalDivider = new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST);
RecyclerView.ItemDecoration horizontalDivider = new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST);
recyclerView.addItemDecoration(horizontalDivider);
recyclerView.addItemDecoration(verticalDivider);
// this is the default;
// this call is actually only necessary with custom ItemAnimators
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(recyclerViewAdapter);
return rootView;
}
/** Creates and returns the data items to be shown in the Recycler View*/
private ArrayList<DataModel> getDataModelList(){
ArrayList<DataModel> dataModels = new ArrayList<>();
for (int i = 0; i < NUM_OF_ITEMS; i++) {
dataModels.add(new DataModel("Title:"+i));
}
return dataModels;
}
}
[/sourcecode]
You can learn more by tweaking the source code of the provided sample application here.
This is all what I learned from my personal study regarding RecyclerView.
Feel free to provide your feedback regarding this discussion.
Happy coding….
waw Great Tutorial