Recycling is very important as waste has a huge negative impact on the natural environment. Same as real world in computer programming world recycling a memory is very important. Programmer dynamically allocates memory to program, when requested, and free it up for reuse when it is no longer needed. In a same way Recycling play a huge role in Android world. For Displaying scrollable list of items Google introduced Listview with Android SDK API 1. But with complex list of data Listview not provide good performance as it not reuse cell (Recycle) while scrolling up/down (We can achieve this using Viewholder concept in Listview). Recyclerview is introduced in API 21(Lollipop) to overcome all this problems which we face in Listview. Recyclerview is game changer for android developer. Recyclerview is advance and improved version of Listview. Today we are going to discuss about Recyclerview in Android.
Recyclerview Architecture
List of Components of Recyclerview
1. Viewholder:
RecyclerView.ViewHolder class is used by adapter to bind view with a position. ViewHolder holds the information of the view. ViewHolder is compulsory when we use RecyclerView. For making ViewHolder extend class with RecyclerView.ViewHolder.
2. Layout Manager:
LayoutManager is also compulsory when we are using RecyclerView. It is responsible for displaying user interface while we are using RecyclerView. There are 3 types of Predefined LayoutManager for RecyclerView.
1. LinearLayoutManager:
This is most common LayoutManager used for Displaying list of data using RecyclerView. Using LinearLayoutManager we can display list of data vertically as well as horizontally.
2. GridLayoutManager:
Using this layout manager we can display data in grid like any photo gallery. Here it gives us lots of flexibility like we can give different span counts.
3. StaggeredGridLayoutManager:
Through this layout manager, we can create staggered lists. Just like the Pinterest screen.
3. ItemDecoration:
As per the official Android Developers Documentation An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.
4. Item Animator:
Animation has touch new heights in real world as well as in Android also. Now imagine if we could add animation in to scrollable list. In ListView there is no special provision for this but in RecyclerView we have ItemAnimation class for animation on items as changes are made to the adapter. Subclasses of ItemAnimator can be used to implement custom animations for actions on ViewHolder items.
Now Let’s make one android project for better understand how we can display scrollable list of data into RecyclerView.
Implementation
Step 1:
Add below dependency into build.gradle
dependencies {
implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
implementation 'com.github.bumptech.glide:glide:4.7.1'
}
Step 2:
Create one layout file which will display list of movies name it as list_item_movie.xml
<?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"
android:background="@android:color/white">
<ImageView
android:id="@+id/imageMovie"
android:layout_width="110dp"
android:layout_height="150dp"
android:contentDescription="@string/app_name"
android:scaleType="fitXY"
android:src="@drawable/ic_avengers" />
<TextView
android:id="@+id/textMovieTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:textColor="@android:color/black"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@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:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:textColor="@android:color/black"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/imageMovie"
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:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:textColor="@android:color/black"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/imageMovie"
app:layout_constraintTop_toBottomOf="@id/textMovieViews"
tools:text="Release Date: 16 Feb 2018" />
</android.support.constraint.ConstraintLayout>
Step 3:
Now create one ViewHolder class by extending RecyclerView.ViewHolder and name it as MovieListViewHolder.
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 Adapter class by extending RecyclerView.Adapter name it as MovieListAdapter.
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 MovieListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var listOfMovies = mutableListOf<MovieModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MovieListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_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.toMutableList()
notifyDataSetChanged()
}
fun addMoreData(listOfMovies: List<MovieModel>) {
this.listOfMovies.addAll(listOfMovies)
notifyDataSetChanged()
}
}
override fun getItemCount(): Int = listOfMovies.size using this method RecyclerView decide how many rows it will display.
What is BindViewHolder?
For Understanding onBindViewHolder() we need to first check how RecyclerView display data into rows. Let us consider we need to display 100 record in RecyclerView. But RecyclerView will not created all 100 rows at same time. Instead of that RecyclerView creates only the 10 views that are on screen. This way you get 10x better speed and memory usage. But what happens when you start scrolling and need to start showing next views?
Again a simple approach would be to create a new view for each new row that you need to show. But this way by the time you reach the end of the list you will have created 100 views and your memory usage would be the same as in the first approach. And creating views takes time, so your scrolling most probably wouldn’t be smooth.
This is why RecyclerView takes advantage of the fact that as you scroll and new rows come on screen also old rows disappear off screen. Instead of creating new view for each new row, an old view is recycled and reused by binding new data to it. This happens exactly in onBindViewHolder(). Initially you will get new unused view holders and you have to fill them with data you want to display. But as you scroll you’ll start getting view holders that were used for rows that went off screen and you have to replace old data that they held with new data.
Step 5:
Now create one class VerticalSpaceItemDecoration and extend it using 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 VerticalSpaceItemDecoration(private val verticalSpaceHeight: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State) {
outRect.bottom = verticalSpaceHeight
}
}
This class is responsible for adding space into every rows.
Step 6:
Create one more class for adding divider dynamically, Name that class as DividerItemDecoration and extend it using RecyclerView.ItemDecoration() class.
package com.android4dev.recyclerview.recyclerview_helper
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.support.v4.content.ContextCompat
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.drawable.Drawable
/***
* Made by Lokesh Desai (Android4Dev)
*/
class DividerItemDecoration
/**
* Default divider will be used
*/(context: Context) : RecyclerView.ItemDecoration() {
private val ATTRS = intArrayOf(android.R.attr.listDivider)
private var divider: Drawable? = null
init {
val styledAttributes = context.obtainStyledAttributes(ATTRS)
divider = styledAttributes.getDrawable(0)
styledAttributes.recycle()
}
/**
* Custom divider will be used
*/
constructor(context: Context, resId: Int) : this(context) {
divider = ContextCompat.getDrawable(context, resId)
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val left = parent.paddingLeft
val right = parent.width - parent.paddingRight
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + divider!!.intrinsicHeight
divider!!.setBounds(left, top, right, bottom)
divider!!.draw(c)
}
}
}
For adding custom divider view we will use below lines
constructor(context: Context, resId: Int) : this(context) {
divider = ContextCompat.getDrawable(context, resId)
}
Step 7:
Now create one Activity and name it as RecyclerViewLinearLayoutActivity.
package com.android4dev.recyclerview
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import com.android4dev.recyclerview.R.id.recyclerViewMovies
import com.android4dev.recyclerview.adapter.MovieListAdapter
import com.android4dev.recyclerview.adapter.MovieListViewHolder
import com.android4dev.recyclerview.base.BaseActivity
import com.android4dev.recyclerview.model.MovieModel
import com.android4dev.recyclerview.recyclerview_helper.DividerItemDecoration
import com.android4dev.recyclerview.recyclerview_helper.VerticalSpaceItemDecoration
import kotlinx.android.synthetic.main.activity_main.*
class RecyclerViewLinearLayoutActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
recyclerViewMovies.layoutManager = LinearLayoutManager(this)
recyclerViewMovies.addItemDecoration(VerticalSpaceItemDecoration(48))
//This will for default android divider
recyclerViewMovies.addItemDecoration(DividerItemDecoration(this))
//This will for custom divider
// recyclerViewMovies.addItemDecoration(DividerItemDecoration(this, R.drawable.drawable_divider_view))
val movieListAdapter = MovieListAdapter()
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
}
}
As we can see first assign LinearLayoutManager with RecyclerView.LayoutManager using recyclerViewMovies.layoutManager = LinearLayoutManager(this). Without LayoutManager RecyclerView will not able to display any list of data. Than create object of the adapter using val movieListAdapter = MovieListAdapter(). After attach adapter with RecyclerView using recyclerViewMovies.adapter = movieListAdapter.
Now we have already created two classes for divider and vertical spaces. Use those classes with RecyclerView like this
//For Vertical Spaces
recyclerViewMovies.addItemDecoration(VerticalSpaceItemDecoration(48))
//For Dividers
recyclerViewMovies.addItemDecoration(DividerItemDecoration(this))
Here in this example we have used LinearLayoutManager for displaying list of movies vertically. Now run the project and you will able to see following result.
Summary
From long time we are using ListView. But after MaterialDesign introduced it is hard to manage List of data using ListView. Because using ListView it is hard to manage complex list. With RecyclerView most of the issues related to ListView has been resolved. If you don’t have complex list of data than it is ok to use ListView but if you have complex list of data than it is always recommended to use RecyclerView In Android.