Building list and grid layout with RecyclerView in Android

    In Android application development, we are used to using ListView and GridView to building a list or grid layout. Up to now, with Material Design has been published, by only one widget called RecyclerView, we can create both of them by changing it's LayoutManager. Moreover, with it, developer also can do some exciting animations with Action Bar when scrolling screen. So that, it's time we have to change to keep pace with the development of design technology.
    Through this post, I will present the way to creating horizontal/vertical list and grid layout with this new widget.

Layout Manager for RecyclerView

    The most important point in building layout type with RecyclerView is initializing it's LayoutManager. For example:
//create a horizontal list view
LinearLayoutManager horizontalManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true);
.
.
//create a vertical list view
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
.
.
//create a grid layout
GridLayoutManager layoutManager = new GridLayoutManager(this, 3); //3 is number of coloumn
    And attatch layout manager to RecyclerView with this method:
recyclerView.setLayoutManager(layoutManager);

Sample horizontal and vertical list view

    In this sample project, I put two type of list view in one screen. Make an activity layout simple like this:
activity_list_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp"
        android:text="Horizontal ListView"
        android:textStyle="bold" />

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp"
        android:text="Vertical ListView"
        android:textStyle="bold" />

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

</LinearLayout>
    And it's programmatically code:
ListViewActivity.java
package info.devexchanges.recyclerview;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

public class ListViewActivity extends AppCompatActivity {

    private RecyclerView horizontalList;
    private RecyclerView verticalList;
    private HorizontalListAdapter horizontalAdapter;
    private VerticalListAdapter verticalAdapter;

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

        horizontalList = (RecyclerView)findViewById(R.id.horizontal_recycler);
        verticalList = (RecyclerView)findViewById(R.id.recyle_view);

        horizontalList.setHasFixedSize(true);
        verticalList.setHasFixedSize(true);

        //set horizontal LinearLayout as layout manager to creating horizontal list view
        LinearLayoutManager horizontalManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        horizontalList.setLayoutManager(horizontalManager);
        horizontalAdapter = new HorizontalListAdapter(this);
        horizontalList.setAdapter(horizontalAdapter);

        //set vertical LinearLayout as layout manager for vertial listview
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        verticalList.setLayoutManager(layoutManager);
        verticalAdapter = new VerticalListAdapter(this);
        verticalList.setAdapter(verticalAdapter);
    }
}
    2 simple RecyclerView adapters in this case (like ListView adapter):
HorizontalListAdapter.java
package info.devexchanges.recyclerview;

import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;

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

    private Activity activity;

    public HorizontalListAdapter(Activity activity) {
        this.activity = activity;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        LayoutInflater inflater = activity.getLayoutInflater();
        View view = inflater.inflate(R.layout.item_horizontal_list, viewGroup, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(HorizontalListAdapter.ViewHolder viewHolder, final int position) {
        viewHolder.linearLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(activity, "Position clicked: " + position, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return 10;
    }

    /**
     * View holder to display each RecylerView item
     */
    protected class ViewHolder extends RecyclerView.ViewHolder {

        private LinearLayout linearLayout;

        public ViewHolder(View view) {
            super(view);
            linearLayout = (LinearLayout) view.findViewById(R.id.layout);
        }
    }
}
VerticalListAdapter.java

package info.devexchanges.recyclerview;

import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

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

    private Activity activity;

    public VerticalListAdapter(Activity activity) {
        this.activity = activity;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = activity.getLayoutInflater();
        View view = inflater.inflate(R.layout.item_recycler_view, parent, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        if ((position + 1) % 2 == 0) {
            viewHolder.imageView.setImageResource(R.drawable.even);
        } else {
            viewHolder.imageView.setImageResource(R.drawable.odd);
        }
        viewHolder.container.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(activity, "Position: " + position, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return 10;
    }

    /**
     * View holder to display each RecylerView item
     */
    protected class ViewHolder extends RecyclerView.ViewHolder {
        private ImageView imageView;
        private RelativeLayout container;

        public ViewHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.image);
            container = (RelativeLayout) view.findViewById(R.id.container);
        }

    }
}
    Running this activity, we'll have this output:

Sample grid view

    Similar with list view, grid layout by RecyclerView is not much complicated. Firstly, designing a layout:
activity_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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">

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

</RelativeLayout>
Simple programmatically code:
GridViewActivity.java
package info.devexchanges.recyclerview;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

public class GridViewActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private GridViewAdapter adapter;

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

        recyclerView = (RecyclerView)findViewById(R.id.recyle_view);
        recyclerView.setHasFixedSize(true);

        //set GridLayoutManager
        GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
        recyclerView.setLayoutManager(layoutManager);
        adapter = new GridViewAdapter(this);
        recyclerView.setAdapter(adapter);
    }
}
    Layout for each grid's item:
item_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dp"
    android:layout_height="120dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/sample_1" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#80000000"
        android:padding="10dp"
        android:textColor="#ffffff" />

</RelativeLayout>
    And the RecyclerView adapter:
GridViewAdapter.java
package info.devexchanges.recyclerview;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

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

    private Activity activity;

    public GridViewAdapter(Activity activity) {
        this.activity = activity;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        LayoutInflater inflater = activity.getLayoutInflater();
        View view = inflater.inflate(R.layout.item_grid, viewGroup, false);

        return new ViewHolder(view);
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(GridViewAdapter.ViewHolder viewHolder, final int position) {
        if (position % 3 == 0) {
            viewHolder.imageView.setImageResource(R.drawable.sample_3);
        } else if (position % 3 == 1) {
            viewHolder.imageView.setImageResource(R.drawable.sample_1);
        } else {
            viewHolder.imageView.setImageResource(R.drawable.sample_2);
        }
        viewHolder.textView.setText("Position: " + (position + 1));
        viewHolder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(activity, "You clicked at position: " + position, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return 10;
    }

    /**
     * View holder to display each RecylerView item
     */
    protected class ViewHolder extends RecyclerView.ViewHolder {

        private TextView textView;
        private ImageView imageView;

        public ViewHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.image);
            textView = (TextView) view.findViewById(R.id.text);
        }
    }
}
    Running it, we have this output:

RecyclerView weaknesses

    Through all codes above, you realize that RecyclerView has some own weakness, not perfect yet:
  • Cannot adding header/footer view easily like ListView (by call addHeaderView/addFooterView method). 
  • It's difficult to handle item clicked event.
  • Cannot "auto column" with grid layout. If you would like to set dynamic column for different devices, please initialize the manager with an integer resource value and provide different values for different screens (i.e. values-w600, values-large, values-land). 
    Even though, it still should be used. Hopefully, in the near future, RecyclerView will be fixed and improved to change itself better and the Material Design technology become more perfect. Moreover, you should read my previous post about RecyclerView and CardView as items to understand the adapter and it's own ViewHolder. Finally, project source code now availble on @Github.

Share


Previous post
« Prev Post
Next post
Next Post »