Android GridLayoutManager Example

Filed Under: Android

Android GridLayoutManager is the RecyclerView.LayoutManager implementation to lay out items in a grid. In this tutorial, we’ll create an application that displays CardViews inside a RecyclerView in the form of a GridLayout. Also, we’ll implement an interface that makes RecyclerView item click similar to a ListView itemClickListener.

Android GridLayoutManager

We’ve implemented a RecyclerView using a LinearLayoutManager here. Now let’s use a GridLayoutManager to layout the RecyclerView as a grid.

Following is the constructor for a GridLayoutManager.


GridLayoutManager (Context context, 
                int spanCount, 
                int orientation, 
                boolean reverseLayout)

reverseLayout if set true then layout items from end to start.

To set the span size for each item, we invoke the method setSpanSizeLookup on the GridLayoutManager

Let’s implement RecyclerView using a GridLayoutManager in a new Android Studio project.

Android GridLayoutManager Example Project Structure

Android GridLayoutManager example

The project consists of a single Activity : MainActivity.java, an adapter class : RecyclerViewAdapter.java, a DataModel.java class and a custom GridLayoutManager class AutoFitGridLayoutManager.java.

The xml layout of the MainActivity.java class is defined in the file activity_main.xml as


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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </RelativeLayout>

</android.support.design.widget.CoordinatorLayout>

Note: Don’t forget to add the following dependencies for Material Design widgets and CardView in the build.gradle file.


compile 'com.android.support:cardview-v7:25.1.1'
compile 'com.android.support:design:25.1.1'

The DataModel.java class is given below:
package com.journaldev.recyclerviewgridlayoutmanager;


public class DataModel {

    public String text;
    public int drawable;
    public String color;

    public DataModel(String t, int d, String c )
    {
        text=t;
        drawable=d;
        color=c;
    }
}

The DataModel class will hold the text, drawable icon and background colour of each item cell.

The RecyclerViewAdapter.java class is given below:


package com.journaldev.recyclerviewgridlayoutmanager;

import android.content.Context;
import android.graphics.Color;
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.TextView;

import java.util.ArrayList;


public class RecyclerViewAdapter extends RecyclerView.Adapter {

    ArrayList mValues;
    Context mContext;
    protected ItemListener mListener;

    public RecyclerViewAdapter(Context context, ArrayList values, ItemListener itemListener) {

        mValues = values;
        mContext = context;
        mListener=itemListener;
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public TextView textView;
        public ImageView imageView;
        public RelativeLayout relativeLayout;
        DataModel item;

        public ViewHolder(View v) {

            super(v);

            v.setOnClickListener(this);
            textView = (TextView) v.findViewById(R.id.textView);
            imageView = (ImageView) v.findViewById(R.id.imageView);
            relativeLayout = (RelativeLayout) v.findViewById(R.id.relativeLayout);

        }

        public void setData(DataModel item) {
            this.item = item;

            textView.setText(item.text);
            imageView.setImageResource(item.drawable);
            relativeLayout.setBackgroundColor(Color.parseColor(item.color));

        }


        @Override
        public void onClick(View view) {
            if (mListener != null) {
                mListener.onItemClick(item);
            }
        }
    }

    @Override
    public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_view_item, parent, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder Vholder, int position) {
        Vholder.setData(mValues.get(position));

    }

    @Override
    public int getItemCount() {

        return mValues.size();
    }

    public interface ItemListener {
        void onItemClick(DataModel item);
    }
}

In the above code we’ve defined an ItemListener interface that’ll be implemented in the MainActivity.java class.

The xml layout for each RecyclerView item is given below.
recycler_view_item.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.v7.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        card_view:cardCornerRadius="0dp"
        card_view:cardElevation="@dimen/margin10"
        card_view:cardMaxElevation="@dimen/margin10"
        card_view:contentPadding="@dimen/margin10">


        <RelativeLayout
            android:id="@+id/relativeLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:layout_gravity="center">

            <ImageView
                android:id="@+id/imageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:tint="@android:color/white"
                android:padding="5dp" />


            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:textColor="@android:color/white"
                android:layout_below="@+id/imageView" />


        </RelativeLayout>

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

</LinearLayout>

The AutoFitGridLayoutManager.java class is given below:


package com.journaldev.recyclerviewgridlayoutmanager;

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

public class AutoFitGridLayoutManager extends GridLayoutManager {

    private int columnWidth;
    private boolean columnWidthChanged = true;

    public AutoFitGridLayoutManager(Context context, int columnWidth) {
        super(context, 1);

        setColumnWidth(columnWidth);
    }

    public void setColumnWidth(int newColumnWidth) {
        if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
            columnWidth = newColumnWidth;
            columnWidthChanged = true;
        }
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (columnWidthChanged && columnWidth > 0) {
            int totalSpace;
            if (getOrientation() == VERTICAL) {
                totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
            } else {
                totalSpace = getHeight() - getPaddingTop() - getPaddingBottom();
            }
            int spanCount = Math.max(1, totalSpace / columnWidth);
            setSpanCount(spanCount);
            columnWidthChanged = false;
        }
        super.onLayoutChildren(recycler, state);
    }
}

The span count is dynamically calculated based on the orientation, width and height available.

The MainActivity.java class is given below:


package com.journaldev.recyclerviewgridlayoutmanager;

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

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.ItemListener {

    RecyclerView recyclerView;
    ArrayList arrayList;

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


        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        arrayList = new ArrayList();
        arrayList.add(new DataModel("Item 1", R.drawable.battle, "#09A9FF"));
        arrayList.add(new DataModel("Item 2", R.drawable.beer, "#3E51B1"));
        arrayList.add(new DataModel("Item 3", R.drawable.ferrari, "#673BB7"));
        arrayList.add(new DataModel("Item 4", R.drawable.jetpack_joyride, "#4BAA50"));
        arrayList.add(new DataModel("Item 5", R.drawable.three_d, "#F94336"));
        arrayList.add(new DataModel("Item 6", R.drawable.terraria, "#0A9B88"));

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, arrayList, this);
        recyclerView.setAdapter(adapter);


        /**
         AutoFitGridLayoutManager that auto fits the cells by the column width defined.
         **/

        /*AutoFitGridLayoutManager layoutManager = new AutoFitGridLayoutManager(this, 500);
        recyclerView.setLayoutManager(layoutManager);*/


        /**
         Simple GridLayoutManager that spans two columns
         **/
        GridLayoutManager manager = new GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(manager);
    }

    @Override
    public void onItemClick(DataModel item) {

        Toast.makeText(getApplicationContext(), item.text + " is clicked", Toast.LENGTH_SHORT).show();

    }
}

  1. The above class implements the interface RecyclerViewAdapter.ItemListener and overrides the method onItemClick that’s defined in the adapter class. By doing this, we’ve implemented the RecyclerView Click Listener within our Activity instead of the Adapter class(similar to the standard onItemClickListener defined for a ListView)
  2. A DataModel class holds the details for each RecyclerView item
  3. The LayoutManager of the RecyclerView can be defined by either instantiating the AutoFitGridLayoutManager class with the column width set as 500 or by invoking the GridLayoutManager class object and setting the number of columns as 2

Let’s see the output of the application with the standard GridLayoutManager code.

android GridLayoutManager grid layout output

As you can see, each row has two items that span the column width in both orientations.

Now comment out the code for simple GridLayoutManager and run the code for AutoFitGridLayoutManager


AutoFitGridLayoutManager layoutManager = new AutoFitGridLayoutManager(this, 500);
recyclerView.setLayoutManager(layoutManager);

The output of the application in action is given below.
android GridLayoutManager auto fit grid layout

As you can see in the above output, when the orientation changes to landscape, each row has three items, thereby dynamically sizing the items to auto-fit the column width.

This brings an end to this tutorial. You can download the final android GridLayoutManager project from the link given below.

Comments

  1. Teddy Ben says:

    Please what can you do to ensure that each item clicked opens a new activity?
    Thanks

  2. Paulo Alexandre Neto Valentim says:

    HI,

    My Adapter gives the error that i should implement the onBindViewHolder method.

    Can you help?
    Please.

  3. wanfadger says:

    The autofit grid is working for me . thanks for the help

  4. Kirill Karmazin says:

    Thanks for the article! Especially for the AutoFitGridLayoutManager class

    1. Anupam says:

      Glad that it helped you. Also I appreciate your efforts in this comment section.

  5. Vinod Prajapat says:

    How To open New Activity on click grid item

  6. Pooja Sehrawat says:

    It show the error: cannot find symbol method setData(Object) , So plz help me

    1. Vidit Babele says:

      Replace that line with
      holder.setData((DataModel) mValues.get(position));

  7. Naresh says:

    Thanks for your post, it’s very useful
    I want to have this Grid items with different heights, can you explain how we can do this.
    As per the above code all card view items have equal heights, can you make all this are with own context heights, like flexible card items.

    1. Kirill Karmazin says:

      You can randomly set different height for your items in adapter. Or you can set a height of an item’s layout as wrap_content so it fit the content. But keep in mind that the overall height of the row is still constant. Items won’t be able to overlap it.

      But if you mean placing items with different height in such way that they do not fit in row’ height (like a mosaic) you will have to use some 3rd party lib.

  8. junaid khan says:

    thankxx for sharing code. this is very helpful for me.

  9. A Friend says:

    You should redo this tutorial – there are so many bad coding habits in it eg. your dataModel:
    don’t use t,d,c as parameters in a function signature!! use expressive names like what the really stand for like text, drawable and color – you can always use this.text = text in the body and keep your codestyle common over ALL files in this tutorial!

    the naming problem is quite huge in this tutorial: recycler_view_item.xml – the relativelayout is a button_wrapper, the drawable the button_icon the textview is the button text or something else…

    and be consistent about using hungarian notation – look at “say mNo” – by jake wharton

    If you do a tutorial like this – make it exceptionally good so that other people can learn Android in a good way.

    I don’t want to bash you – if you are doing something you gotta be doing it right!

    1. Kirill Karmazin says:

      Every time you name variables as “t”,”k” etc. somewhere on Earth Robert C. Martin is crying.

  10. Ed says:

    I am having trouble starting an activity from the gridview.

  11. Joshua Kisanga says:

    Thanks so much, Very good gridviewLayout.

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