GridLayoutManager


GridLayoutManager
is a part of the RecyclerView.LayoutManager. For displaying list of data in Grid like Photo Gallery we can use GridLayoutManager. In Previous Blog we have discussed What is  RecyclerView? Today we are going to learn about How to display list of data in RecyclerView Using GridLayoutManager. Before we start with example the first question you all want to ask is

 

What is different between GridLayoutManager and LinearLayoutManager?

# LinearLayoutManager:

  • Using LinearLayoutManager we can generate simple list of data same as ListView.
  • It fully occupy the row. But each row have same height and width.

# GridLayoutManager:

  • Using GridLayoutManager we can split row more than one times.
  • We may have more than 2 column but each have same height and width.

Hope you will get basic idea about differences.

Now let’s start with a simple example of GridLayoutManager

Implementation


Step 1:

Add following dependency in build.gradle for RecyclerView.

dependencies {
   implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
}


Step 2:

Make one layout file and name it as list_item_grid_movie.xml . Add following code into layout file.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="wrap_content">

    <ImageView
        android:id="@+id/imageMovie"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:contentDescription="@string/app_name"
        android:scaleType="fitXY"
        android:src="@drawable/ic_avengers"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textMovieTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="@id/imageMovie"
        app:layout_constraintTop_toBottomOf="@id/imageMovie"
        tools:text="Avengers" />

    <TextView
        android:id="@+id/textMovieViews"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:textColor="@android:color/black"
        android:textSize="14sp"
        app:layout_constraintStart_toStartOf="@id/textMovieTitle"
        app:layout_constraintTop_toBottomOf="@id/textMovieTitle"
        tools:text="Views: 100" />

    <TextView
        android:id="@+id/textReleaseDate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:textColor="@android:color/black"
        android:textSize="14sp"
        app:layout_constraintStart_toStartOf="@id/textMovieViews"
        app:layout_constraintTop_toBottomOf="@id/textMovieViews"
        tools:text="Release Date: 16 Feb 2018" />

</android.support.constraint.ConstraintLayout>


Step 3:

Now create one class name it as MovieListViewHolder extend it using RecyclerView.ViewHolder. As name suggest this Holder class will hold View information.

package com.android4dev.recyclerview.adapter

import android.support.v7.widget.RecyclerView
import android.view.View
import com.android4dev.recyclerview.model.MovieModel
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.list_item_movie.view.*
import java.util.*

class MovieListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bindView(movieModel: MovieModel) {
        itemView.textMovieTitle.text = movieModel.movieTitle
        itemView.textMovieViews.text = "Views: " + movieModel.movieViews
        itemView.textReleaseDate.text = "Release Date: " + movieModel.releaseDate

        Glide.with(itemView.context).load(movieModel.moviePicture!!).into(itemView.imageMovie)
    }

}

 

Step 4:

Now create one more class name it as MovieListGridRecyclerAdapter and extend it using RecyclerView.Adapter.

package com.android4dev.recyclerview.adapter

import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.android4dev.recyclerview.R
import com.android4dev.recyclerview.model.MovieModel

class MovieListGridRecyclerAdapter:RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private var listOfMovies = listOf<MovieModel>()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return MovieListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_grid_movie, parent, false))
    }

    override fun getItemCount(): Int = listOfMovies.size

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
        val movieViewHolder = viewHolder as MovieListViewHolder
        movieViewHolder.bindView(listOfMovies[position])
    }

    fun setMovieList(listOfMovies: List<MovieModel>) {
        this.listOfMovies = listOfMovies
        notifyDataSetChanged()
    }
}

 

1. getItemCount() will return number of list items.
2. onCreateViewHolder() only creates a new view holder when there are no existing view holders which the RecyclerView can reuse. So, for instance, if your RecyclerView can display 5 items at a time, it will create 5-6 ViewHolders, and then automatically reuse them, each time calling onBindViewHolder.
3. onBindViewHolder() this method will bind view with adapter class.


Step 5:

Create class for as GridItemDecoration extend it with RecyclerView.ItemDecoration(). 

package com.android4dev.recyclerview.recyclerview_helper

import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.View

/***
 * Made by Lokesh Desai (Android4Dev)
 */
class GridItemDecoration(gridSpacingPx: Int, gridSize: Int) : RecyclerView.ItemDecoration() {
    private var mSizeGridSpacingPx: Int = gridSpacingPx
    private var mGridSize: Int = gridSize

    private var mNeedLeftSpacing = false

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val frameWidth = ((parent.width - mSizeGridSpacingPx.toFloat() * (mGridSize - 1)) / mGridSize).toInt()
        val padding = parent.width / mGridSize - frameWidth
        val itemPosition = (view.getLayoutParams() as RecyclerView.LayoutParams).viewAdapterPosition
        if (itemPosition < mGridSize) {
            outRect.top = 0
        } else {
            outRect.top = mSizeGridSpacingPx
        }
        if (itemPosition % mGridSize == 0) {
            outRect.left = 0
            outRect.right = padding
            mNeedLeftSpacing = true
        } else if ((itemPosition + 1) % mGridSize == 0) {
            mNeedLeftSpacing = false
            outRect.right = 0
            outRect.left = padding
        } else if (mNeedLeftSpacing) {
            mNeedLeftSpacing = false
            outRect.left = mSizeGridSpacingPx - padding
            if ((itemPosition + 2) % mGridSize == 0) {
                outRect.right = mSizeGridSpacingPx - padding
            } else {
                outRect.right = mSizeGridSpacingPx / 2
            }
        } else if ((itemPosition + 2) % mGridSize == 0) {
            mNeedLeftSpacing = false
            outRect.left = mSizeGridSpacingPx / 2
            outRect.right = mSizeGridSpacingPx - padding
        } else {
            mNeedLeftSpacing = false
            outRect.left = mSizeGridSpacingPx / 2
            outRect.right = mSizeGridSpacingPx / 2
        }
        outRect.bottom = 0
    }
}

GridItemDecoration class will help us to manage same space between all RecyclerView items.

Tips : Don’t use padding for managing spacing between grid items always use ItemDecoration. Avoid to give padding for allocating space between grid items because some times it generates performance issue.

 

Step 6:

Now create one activity class and Name it as RecyclerViewGridLayoutActivity.

package com.android4dev.recyclerview

import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import com.android4dev.recyclerview.adapter.MovieListGridRecyclerAdapter
import com.android4dev.recyclerview.base.BaseActivity
import com.android4dev.recyclerview.model.MovieModel
import com.android4dev.recyclerview.recyclerview_helper.GridItemDecoration
import kotlinx.android.synthetic.main.activity_main.*

class RecyclerViewGridLayoutActivity : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()
    }

    private fun initView() {
        recyclerViewMovies.layoutManager = GridLayoutManager(this,2)

        //This will for default android divider
        recyclerViewMovies.addItemDecoration(GridItemDecoration(10, 2))

        val movieListAdapter = MovieListGridRecyclerAdapter()
        recyclerViewMovies.adapter = movieListAdapter
        movieListAdapter.setMovieList(generateDummyData())
    }

    private fun generateDummyData(): List<MovieModel> {
        val listOfMovie = mutableListOf<MovieModel>()

        var movieModel = MovieModel(1, "Avengers", 500, "16 Feb 2018", R.drawable.ic_avengers)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(2, "Avengers: Age of Ultron", 400, "17 March 2018", R.drawable.ic_avengers_2)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(3, "Iron Man 3", 550, "17 April 2018", R.drawable.ic_ironman_3)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(4, "Avengers: Infinity War", 1500, "15 Jan 2018", R.drawable.ic_avenger_five)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(5, "Thor: Ragnarok", 200, "17 March 2018", R.drawable.ic_thor)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(6, "Black Panther", 250, "17 May 2018", R.drawable.ic_panther)
        listOfMovie.add(movieModel)

        movieModel = MovieModel(7, "Logan", 320, "17 Dec 2018", R.drawable.ic_logan)
        listOfMovie.add(movieModel)

        return listOfMovie
    }
}

 

In this Activity class we will attach Adapter class with RecyclerView.
Now we will assign GridLayoutManager(2 Column) to RecyclerView.LayoutManager using below line.
recyclerViewMovies.layoutManager = GridLayoutManager(this,2)
Here 2 is number of columns. We can assign more than 2 column also.

 

Step 7:

 Create layout file for RecyclerViewGridLayoutActivity class.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:background="@android:color/white"
    tools:context=".RecyclerViewLinearLayoutActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerViewMovies"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:overScrollMode="never"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout


Now run the project and wait for the result.

 

 

Conclusion

With a little effort we have achieved RecyclerView Using GridLayoutManager. Hence, we can also achieve this using GridView. But it depends of requirements which suits best for you.

Source Code

Check out Github Project Which Contains All RecyclerView Examples

You Can Also Refer

Hi, I am Android Developer and Founder of Android4Dev. Right now my interest is in RxKotlin, MVVM and Dagger2. I am Kotlin Lover. Kotlin made me more productive in terms of coding. Do you want me to code for you ? Please don’t hesitate to contact me.