RecyclerView Implementation. Part — 3.2.

Dhruvam Sharma
AndroidPub
Published in
10 min readJan 17, 2018

Hi there, I am Dhruvam at your service again. If you’re joining me from this tutorial, kindly read the part -1 and part-2 and part-3.1 too and then start with this one. With this tutorial, specifically, we are going to implement RecyclerView in Java. This is the most updated tutorial on RecyclerView and the one you should definitely read and bookmark. ;)

Before we begin, I would love for you to try my latest gaming application that implements all of the features that I am going to talking in this series. Any feedback is appreciated. Open this link in your mobile, if possible.
Try it:
Gamezop

Gamezop — A Gaming Platform.

So we’re done with implementing the RecyclerView but there are still some things left I needed to tell you about.

Details. Details. Details.

So I told you in the second part of the series, that while displaying the data, the RecyclerView will initially create X number of Views and ViewHolders (where X is the number of rows or views that can be fitted at one time on the screen or the view-port). And then when you start scrolling, You’ll see that at most, it will create just 4 more Views and ViewHolders. Let’s get this into action and understand how this happens.

LayoutManagers — The answer behind Confusion.

See this picture.

The Layout Manager is used for displaying data on the list and managing all the views. How does it do that?
First things first. The layout Manager is given access to the Recycler instance (A thing which handles the place where removed views are kept) at certain points to recycle old views or obtain new views from a potentially recycled previous child.
Removed? WTF?

We’ll come to this back again when you yourself will be cleared with any doubts you have. Have patience and just follow my lead.

Two ways to handle ChildViews during updating Layout

While scrolling, many functions happen. I’ll try to explain them all. When the Views go out of the view-port, these views can either be:

  1. Detached
  2. Removed

Detached
This is a lightweight operation for reordering views. And these views which are detached are expected to be re-attached before code returns.
Detached operation is used to modify indices of attached childviews without rebinding or recreating views through Recycler. (didn’t get it? Don’t worry)

Bear with me. Don’t go away just now.Everything will be clear. Just read carefully.

Removed
It is an operation meant for views that are no longer needed. So any view that is permanently removed should be placed in the Recycler for later reuse.

Stay with me again. Don’t jus scream.

The Recycler Caching System

Whenever the views go out of view-port, there is something to be done with them so that they can be used again. Then caching happens. Caching phenomena helps in regenerating the views from previous views such that creation of views happen only in the worst case scenario.

The Recycler has 2 Level Caching System -
1. Scrap Heap
2. Recycle Pool

Scrap Heap (A place for detached Views)

It is a light weight collection where views can be returned to Layout Manager directly without passing the view back to Adapter again. This is because the view isn’t dirty or we can say that the data is still attached to the ViewHolder. So no need to pass it back to the Adapter for binding the data.
Views placed here, are temprorily detached but will be reused within the same layout pass.

*The View just above and below the View port are the Detached Views. For experimental explanation, I’ve used log statement in the code of previous article. I’ll explain again later below*

Recycle Pool

It is a collection which consists of views that are assumed to have incorrect data (data from a different position or index) also called the dirty view, is always passed to the adapter back so that data can be attached or bound again to the ViewHolder and then returned to the Layout Manager.

Remember this picture? This is how it happens.

Now the Full Process

So there is data kept in the data-set. The adapter binds data to the View and then gives it to the Layout Manager which happens to control the views.
I told you that Recycler instance is provided to the Layout Manager at some points so that it can obtain new views or recycle old views. Let’s see how that happens.

So to display data on screen, we need a row or a view. This view is controlled by the Layout Manager, right? So when a Layout Manager asks for a New View to display it on the screen, it does the following:

Step 1 : Recycler instance checks the scrap heap for a matching position or id. If the view is present in the scrap heap, it is returned to the Layout Manager without re-binding the data or without passing it to the adapter. (As I told earlier).

Step 2 : If the Recycler instance isn’t able to find the appropiate View in the scrap heap, it will instead pull a suitable view from the Recycler Pool and bind the necessary data to it from the adapter in the method- onBindViewHolder().

Step 3 : In case there is no valid view in the Recycler Pool too, then a new View will be created and then data will be be rebound and returned to the Layout Manager (the worst case scenario).

So now go back and read the part Detail.Detail.Detail again for a clear understanding.

Experimental Explanation

Step 1: If you run the application, and open the console, You’ll see some continuous statements like this one after another-
1. in onCreateViewHolder
2. in View Holder
3. in onBindViewHolder

This happens because, currently there are no views in the Scrap Heap or the Recycle Pool because the app has just started. So it creates X number of views that are just required to fit in the view port which logs out 3 statements as mentioned above in relation to the steps explained above.

Step 2: Now when you scroll down, you’ll see in the console that again these 3 statements are executed and that too just 3 or 4 times (keep in mind that you add at least 15 entries so that you view port is filled. If you don’t and your view port is empty, the RecyclerView will just create those number of views and no need of extra 3 or 4 views because there is no scrolling required).

Step 3: If you scroll now, the log will show the out put something like this.

But if you keep scrolling slowly and notice the console, you’ll see that it not always print out the onBindViewHolder. It waits for just one scroll, and then prints out the onBindViewHolder statement.
This is because at this point the views are being taken out from the Scrap Heap where data is already attached to the ViewHolder. So no need to pass it to the adapter. That is why this one Log statement (in OnBindViewHolder) is not executed.

Step 4: If you continue scrolling up and down, you’ll see this statement again and again (in OnBindViewHolder).

So the explanation is done with

We’re still left with using a horizontal List and implementing onClickListsners.

4. Horizontal Lists (numbering from the previous article ;))

If you want to use a horizontal List, you need to do nothing to any of the file except just one.
MainActivity.java

package dell.recylerviewpart1;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

private ArrayList<ContactPOJO> mArrayList = new ArrayList<>();

private RecyclerView mRecyclerView2;
private CustomContactAdapter mAdapter;

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


mRecyclerView2 = findViewById(R.id.recyclerView2);

mAdapter = new CustomContactAdapter(mArrayList);
/* .... Start of edited line.... */
mRecyclerView2.setLayoutManager(new LinearLayoutManager(getApplicationContext(),LinearLayoutManager.HORIZONTAL,false));
/* ...... End of edited line........ */
mRecyclerView2.setItemAnimator( new DefaultItemAnimator());
mRecyclerView2.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
mRecyclerView2.setAdapter(mAdapter);



prepareData();
}

private void prepareData() {
ContactPOJO contact = null;
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);

contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);

mAdapter.notifyDataSetChanged();
}



}

If you see the code, everything is same except just one line. The Layout Manager Line.This is because- I told you that Layout Manager is in full control of the views. So it decides how to display them. So just one liner difference.

You can run the app again and see it for yourself. This is how it should look.

I’ve scrolled it in the middle.

5. onClickListeners

I told you in my previous tutorial of the series that ViewHolder pattern brings in the magic for the RecyclerView. So all the heavy lifting is done in the View Holder.
ListViews had two option for implementing the on-click events. One through the list view itself and one through the each row. It was a little bit confusing.Because RecyclervIew gave power to ViewHolder, it seemed much better to add the functionality to view holder itself.

Let’s see how it’s done. What we want to achieve is, when we click on one of the TextViews, we can see it’s position in a Toast.

It appears like this.

Let’s dive in.

Step 1:

We need to create an interface first. Let’s name it OnRecyclerClickListner and create it in the app/java/dell.recyclerviewpart1.

It will have just one method. onRecyclerViewItemClicked. You van name it anything you want. See the code for a better picture.

package dell.recylerviewpart1;

/**
* Created by dell on 16-01-2018.
*/

public interface OnRecyclerClickListener {
/**
* Called when any item with in recyclerview or any item with in item
* clicked
*
*
@param position
* The position of the item
*
@param id
* The id of the view which is clicked with in the item or
* -1 if the item itself clicked
*/
public void onRecyclerViewItemClicked(int position, int id);
}

Step 2:

We just need to change the CustomContactAdapter to use the method of the Interface. As I told, only the ViewHolder handles the changes, we can do this by changing just the ViewHolder.

public class MyViewHolder extends RecyclerView.ViewHolder {
TextView name, number, addedOn;
public MyViewHolder(View itemView) {
super(itemView);
Log.v("ViewHolder","in View Holder");
name = itemView.findViewById(R.id.textView);
number = itemView.findViewById(R.id.textView2);
addedOn = itemView.findViewById(R.id.textView3);

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onRecyclerViewItemClicked(getAdapterPosition(),view.getId());
}
});

}

When you see the listner in the line, you need to see the full code. So while passing in the data from the MainActivity.java, we also passed a new Listener of the Interface we made earlier. So we need to catch that listener too in the Adapter constructor.
The code so becomes,

package dell.recylerviewpart1;

import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

/**
* Created by dell on 16-01-2018.
*/

public class CustomContactAdapter extends RecyclerView.Adapter<CustomContactAdapter.MyViewHolder> {

private ArrayList<ContactPOJO> arrayList = new ArrayList<>();
private OnRecyclerClickListener listener;

public CustomContactAdapter(ArrayList<ContactPOJO> arrayList, OnRecyclerClickListener listener) {
this.listener = listener;
this.arrayList = arrayList;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.v("CreateViewHolder", "in onCreateViewHolder");
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_list_layout,parent,false);

return new MyViewHolder(itemView);
}



@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
Log.v("BindViewHolder", "in onBindViewHolder");
ContactPOJO contact = arrayList.get(position);
holder.name.setText(contact.getmName());
holder.number.setText(contact.getmNumber());
holder.addedOn.setText(contact.getmAddedOn());

holder.name.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onRecyclerViewItemClicked(position,view.getId());
}
});
}



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

public class MyViewHolder extends RecyclerView.ViewHolder {
TextView name, number, addedOn;
public MyViewHolder(View itemView) {
super(itemView);
Log.v("ViewHolder","in View Holder");
name = itemView.findViewById(R.id.textView);
number = itemView.findViewById(R.id.textView2);
addedOn = itemView.findViewById(R.id.textView3);

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onRecyclerViewItemClicked(getAdapterPosition(),view.getId());
}
});

}


}
}

If you see the code, there’s only few lines changed. The constructor, the new field just under the class implementation and the ViewHolder Constructor. That’s it.

Step 3:

Now we need to pass in the Listener from the Main Activity with implementation of the same. This is done by adding just 3–4 lines of code. See below. I hope you get it fully.

package dell.recylerviewpart1;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

private ArrayList<ContactPOJO> mArrayList = new ArrayList<>();
private RecyclerView mRecyclerView1;

private CustomContactAdapter mAdapter;

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

mRecyclerView1 = findViewById(R.id.recyclerView);

/*........ The new Line to be added .........*/
mAdapter = new CustomContactAdapter(mArrayList, new OnRecyclerClickListener() {
@Override
public void onRecyclerViewItemClicked(int position, int id) {
Toast.makeText(getApplicationContext(),""+position,Toast.LENGTH_SHORT).show();
}
});
mRecyclerView1.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
mRecyclerView1.setItemAnimator( new DefaultItemAnimator());
mRecyclerView1.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
mRecyclerView1.setAdapter(mAdapter);





prepareData();
}

private void prepareData() {
ContactPOJO contact = null;
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);

contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);
contact = new ContactPOJO("Dhruvam","9467884671","22/12/1995");
mArrayList.add(contact);

mAdapter.notifyDataSetChanged();
}



}

Rest every thing is just the same! Finally, we’re finish with this tutorial too.

I hoped you like this tutorial. I have made it as easier I could have. If I am still unclear at any point, comment in the comment section and if you like it, please Clap or Review. Thank you in advance :D

--

--

Dhruvam Sharma
AndroidPub

Google-certified Android Developer @Unikon. Android Geek. In love with Flutter. Blockchain Explorer. Dancer. 🕺 Reader. Coffee Addict. 😍