Over a million developers have joined DZone.

Android RecyclerView Tutorial

It is important to know how to use Android RecyclerView and how to customize its behavior. This tutorial will cover some important aspects about Android RecyclerView giving all the information you need to build great Android app using it.

· Mobile Zone

Android RecyclerView is an important component that is present often in Android apps. It is important to know how to use it and how to customize its behavior. This tutorial will cover some important aspects about Android RecyclerView giving all the information you need to build great Android app using it. As you may already know, Android RecyclerView is a new component introduced in Android Lollipop. This component increases performances respect to Android ListView. Moreover, respect to Android ListView, Android RecyclerView is much more customizable. In this tutorial, we will learn how to:

  • use Android RecyclerView with a custom layout
  • intercept user events
  • customize RecyclerView appearance

Getting Started With Android RecyclerView

Android RecyclerView uses an Adapter to represent the information we want to show as a list. Even in Android ListView we use an adapter. Anyway, Android RecyclerView extends this concept and improves the way this adapter is used to increase overall performances.The adapter is a class that extends RecyclerView.Adapter and stands behind the UI and the underlying data we want to represent. For this reason, we have to customize it to use our class that represents the information.To start using Android RecyclerView we have to import the support library in the gradle file:

dependencies { .... compile 'com.android.support:recyclerview-v7:24.1.1'}

Now, we are ready to use the RecyclerView UI component. To make things simple, we will use, as data layer, a simple class that represents the country and the respective population:

public class Country { 
    protected String name; 
    protected double population; 
    public Country(String name, double population) { 
        this.name = name; 
        this.population = population; 
    }
}

The next step, is defining it in the android layout file:

<RelativeLayout
    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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.survivingwithandroid.recyclerview.MainActivity"
    tools:showIn="@layout/activity_main">

   <android.support.v7.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:scrollbars="vertical"/>

</RelativeLayout>

and get the reference in our class so that we can use it:

RecyclerView rv = (RecyclerView) findViewById(R.id.recycler_view);

At this moment, the Android RecyclerView is empty, we have to implement our adapter.

public class CountryAdapter extends RecyclerView.Adapter {
    private List countryList; 

    /** * View holder class * */ 
    public class MyViewHolder extends RecyclerView.ViewHolder { 
        public TextView countryText; public TextView popText; 
        public MyViewHolder(View view) { 
            super(view);
            countryText = (TextView) view.findViewById(R.id.countryName); 
            popText = (TextView) view.findViewById(R.id.pop); 
        } 
    } 

    public CountryAdapter(List countryList) { 
        this.countryList = countryList;
    }

    @Override 
    public void onBindViewHolder(MyViewHolder holder, int position) { 
        Country c = countryList.get(position); 
        holder.countryText.setText(c.name);
        holder.popText.setText(String.valueOf(c.population));
    } 

    @Override
    public int getItemCount() { 
        return countryList.size();
    } 

    @Override 
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
        View v = LayoutInflater.from(parent.getContext()) 
        .inflate(R.layout.row,parent, false); 
        return new MyViewHolder(v);
    }
}

There are some important methods we have to override to customize the Android RecyclerView behavior. First of all, notice at the beginning, we have created a simple class (MyViewHolder) that holds the references to the row UI components. This class implements the View-Holder pattern. Next, in onCreateViewHolder, we inflate the row layout returning an instance of the class MyViewHolder. In this simple example, the row layout is:

<RelativeLayout
    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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.survivingwithandroid.recyclerview.MainActivity"
    tools:showIn="@layout/activity_main">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical"/>

</RelativeLayout>

Finally, we override onBindViewHolder that binds our data to the UI. This method, simply, retrieves the data from an ArrayList. According to the current index position it stores the values into the MyViewHolder.Now it is time to set the adapter into the Android RecyclerView:

CountryAdapter ca = new CountryAdapter(countryList);rv.setAdapter(ca);

Don't forget to set the layout manager:

LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
rv.setLayoutManager(llm);

Running the example, we have:

android recyclerview tutorial

Item onClick Listener

Now that our component is configured and shows the information as a list, it is time to handle user clicks on RecyclerView items. In other words, we want to know if  a user clicks on an item and what type of click he is making: simple click or long click.When we use Android RecyclerView the things are a little more complex than Android ListView. In this case, we have to create a class that implements RecyclerView.OnItemTouchListener.

public class RecyclerItemListener implements RecyclerView.OnItemTouchListener { 
    private RecyclerTouchListener listener; 
    private GestureDetector gd; 
    public interface RecyclerTouchListener { 
        public void onClickItem(View v, int position) ; 
        public void onLongClickItem(View v, int position); } 
    public RecyclerItemListener(Context ctx, final RecyclerView rv, final RecyclerTouchListener listener) { 
        this.listener = listener; 
        gd = new GestureDetector(ctx, new GestureDetector.SimpleOnGestureListener() { 
            @Override public void onLongPress(MotionEvent e) { 
                // We find the view 
                View v = rv.findChildViewUnder(e.getX(), e.getY()); 
                // Notify the even
                listener.onLongClickItem(v, rv.getChildAdapterPosition(v)); 
            }

            @Override 
            public boolean onSingleTapUp(MotionEvent e) {
                View v = rv.findChildViewUnder(e.getX(), e.getY()); 
                // Notify the even 
                listener.onClickItem(v, rv.getChildAdapterPosition(v)); 
                return true; 
            }
        });
    } 

    @Override 
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 
        View child = rv.findChildViewUnder(e.getX(), e.getY()); 
        return ( child != null && gd.onTouchEvent(e)); 
    } 

    @Override 
    public void onTouchEvent(RecyclerView rv, MotionEvent e) { } 

    @Override 
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
}

We define a callback interface to notify the listener when the user clicks on an item. The first thing is creating an instance of GestureDetector. In this way  we can know when a user clicks and what click type he is doing. It is important to note that our class overrides onInterceptTouchEvent to know if the user selects and item and if the gesture detected is handled by our instance.To receive notification in the listener class, we implement the interface declared above:

rv.addOnItemTouchListener(
new RecyclerItemListener(getApplicationContext(), rv,
new RecyclerItemListener.RecyclerTouchListener() { 
    public void onClickItem(View v, int position) { 
        System.out.println("On Click Item interface"); 
    } 
    public void onLongClickItem(View v, int position) { 
        System.out.println("On Long Click Item interface"); 
    }
}));


How to Customize Android RecyclerView

The next step is customizing the Android RecyclerView. There are several aspects that can be customized. To do it we have to implement a custom class that extends RecyclerView.ItemDecoration. As first example, we will change the spacing between the Recyclerview row:

public class VerticalSpacingDecoration extends RecyclerView.ItemDecoration { 
    private int spacing; 
    public VerticalSpacingDecoration(int spacing) {
        this.spacing = spacing; 
    } 

    @Override 
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 
        outRect.bottom = spacing;
    }
}

In this example, we have overridden getItemOffsets implementing how spacing. Moreover, we have to tell to the RecyclerView that we want to use a custom decoration:

rv.addItemDecoration(new VerticalSpacingDecoration(64));

Running the example we have:

android recyclerview spacingIn conclusion, we will add another Recyclerview customization: row divider. In this case, Android RecyclerView is different from Android ListView because we have to implement a custom decorator. This is very simple:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private Drawable mDivider;

    public DividerItemDecoration(Drawable divider) {
        this.mDivider = divider;
    }

    @Override
    public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        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(canvas);
        }
    }
}

In this class, we override onDrawOver and implements our UI customization. To set this decorator:

rv.addItemDecoration( 
  new DividerItemDecoration(ContextCompat.getDrawable(getApplicationContext(), 
                            R.drawable.item_decorator)));

where item_decorator is defined under the drawable directory in this way:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
      <size android:height="1dp" />
      <solid android:color="#ff992900" />
</shape>

Running the example we have:

android recyclerview decoratorSource code available @github.

At the end of this post, hopefully, you gained the knowledge about Android RecyclerView and how to customize it according to your needs.

Topics:
android ,tutorial

Published at DZone with permission of Francesco Azzola, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}