RecyclerView Activity implementation.

Introduction

  • This series is going to be dedicated to the basic to Android development. Join me and let us try to build and understand some cool stuff. I used the [Android documentation]. All the resources I used to create this post can be found on ticketnote or HERE. With that being said lets just jump into it.

YouTube Version

Implementing a RecyclerView

  • When implementing a RecyclerView inside of an activity there are 4 main steps you have to follow.

1) Add the RecyclerView to the activity's .XML file

2) Create the individual ViewHolder item.

3) Implementing a Adapter

4) Initializing everything inside of the Activity

  • For each of the four steps I will paste the appropriate code and then we will walk through it line by line.

What is a RecyclerView ?

  • Before we go any further I need to make sure that we are on the same page when it comes to understanding what a RecyclerView is. Basically a RecyclerView is just a view, no different than any other view, so we can add it in an XML file. What makes a RecyclerView special is that it easily and effectively displays large sets of data. All we have to do is supply the data and define how the individual item looks and the RecyclerView 'LIBRARY'(not the class) dynamically creates the elements when they are needed. So anytime we talk about a method being called, it is the RecyclerView library that is doing it. Now that we both have a solid understanding of what a RecyclerView is, lets move on.

1) Add the RecyclerView to the activity's .XML file

<androidx.constraintlayout.widget.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"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/example_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • This is our main activity, the main activity gets automatically created for us. If you are unfamiliar with all these weird names, let me give you a little rundown. A ConstraintLayout is just a container that allows us extra customization, however, we don't actually use this customization. The xmlns: stands for xml-namespace and it is how we define resources to certain namespaces. android, app and tools are all namespaces and the URIs that they are set to equal the resources that they have access to. You can see that we set the width and height on ConstraintLayout to match_parent meaning it is set to the whole screen. The tools:context=".MainActivity" is declaring which activity is associated with this XML file by default.
  • Then we get to the actual RecyclerView, we add a android:id so we can reference it later in our code. The layout width and height are set to match parent so it will stretch to the whole screen.

2) Create the individual ViewHolder item.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/animalName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>
  • Now this is going to act as the individual item. As I stated earlier one of the things that we need to do is create how the individual item is going to look. We do this by creating a new Layout Resource File. To do this go inside the resource folder and click file --> new --> Layout Resource File. The file name can be whatever you want, make sure to entirely delete the default Root element then just add LinearLayout hit ok and you should have a new Layout Resource File. Make sure that the android:orientation="horizontal" is set to horizontal. This makes sure that the imbedded view is displayed horizontal. Also make sure android:layout_height="wrap_content" I believe that the default is match_parent, if you set it to match_parent one item will take up the entire screen. Then add a TextView, this is what is going to hold the individual item for each piece of data that we provide to the RecyclerView. We give it an android:id="@+id/animalName" so we can reference it later for data binding. Also make sure that the layout width and height are set to wrap_content. This assures that the item is no bigger than it has to be. A TextView is just a View that is used to display text to the user. A LinearLayout is just a ViewGroup that alights all its children in a single line, either horizontally or vertically, for us its horizontally. Lets move on

3) Implementing a Adapter

  • First we should start with a solid definition on what a adapter is . Well, in the RecyclerView library an adapter provides a binding for app specific data set to views that are displayed within a RecyclerView. Now lets dive into the code.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private List<String> data;


    public CustomAdapter( List<String> data){
        this.data = data;
    }


    @Override
    public ViewHolder onCreateViewHolder(  ViewGroup parent, int viewType) {
        //this is where we inflate the recycler_view.xml
        View views = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_view,parent,false);

        return new ViewHolder(views);
    }

    @Override
    public void onBindViewHolder( CustomAdapter.ViewHolder holder, int position) {
        String animal = this.data.get(position);
        holder.myTextView.setText(animal);
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder{
        TextView myTextView;
        public ViewHolder(View view){
            super(view);
            myTextView = view.findViewById(R.id.animalName);
        }
    }
}

-This might seem like a lot but lets just take is slow and go line by line and try to understand what is going on.

public class CustomAdapter extends RecyclerView.Adapter

  • This is a public class, which means that anything can access it. Then the CustomAdapter extends RecyclerView.Adapter, the extends keyword signifies that it is a subclass of the RecyclerView.Adapter class. The <CustomAdapter.ViewHolder> is how we use generics in Java. The reason that we have to do this is because of Bounded Type Parameters. If we look at the documentation of RecyclerView.Adapter we find this RecyclerView.Adapter<VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> and this is a clear example of bounded type parameters in Java. Now what this is actually saying is that it accepts any ViewHolder object or its subclass. We will see shorty that we indeed create a ViewHolder subclass. HERE is a link to the official bounded type parameters documentation.

The data

private List<String> data;


    public CustomAdapter( List<String> data){
        this.data = data;
    }
  • This is The data that our adapter is going to use in order to create the ViewHolder objects. We create a private data variable of type List. We use the List interface to allow our code more flexibility in what kind of data we can store. tells the compiler that we only want strings to be inside the list. Then the constructor which just takes in data and assigns it to the private List interface.

ViewHolder class

public static class ViewHolder extends RecyclerView.ViewHolder{
        TextView myTextView;
        public ViewHolder(View view){
            super(view);
            myTextView = view.findViewById(R.id.animalName);
        }
    }
  • With this code block the first two things that you probably noticed was 1) it is a nested class and 2) it is static. So first lets start off with why it is nested? Well in Java you nest one class inside of another because the nested class is highly related to the outer class(which is true for us). Then comes the second question, why is it static? Well first it should be pointed out that in Java a class can only be static if it is a nested class. A nested static class can only reference other static fields from the other class. In general a static class is used to remove some dependency from the outer class. In Android we make it static to prevent memory leaks.

  • Before we talk the TextView I want to talk about the super(view) call in the constructor. Why is is there? Any time in Java when we are extending something which we are with this class, the constructor must make a super() call of some sort. If we do not define a super() call then the Java compiler will make one for us. If you delete the super(view) from our code you will get an error that will say There is no default constructor available in ViewHolder. This proves that when we do not define our own constructor call, Java does it for us. Why do we need this constructor call? The super() call makes sure our super class is instantiated before our own class, just incase we use any super methods or variables.

  • Now lets talk about the TextView and why we are referencing it. If you are unfamiliar with TextView, it is just a View in Android that is used to display a text. We are using it because when our ViewHolder object gets created, we want to have a reference to what we bind our data to. Later on you will see how objects created with this class get passed around the Adapter's methods, that demonstration should give you a better understanding of why is is needed. Now what is the view.findViewById(R.id.animalName);? The view part will be an inflated(created) view that gets passed in by the adapter(more on this late).findViewById does exactly what it says it does, it finds a View by is id. ok but what is R.id.animalName? 'R' stands for resources and it is an auto generated class that contains everything that can be accessed inside of our resource file. id.animalName comes from us defining an id on our TextView when we created our individual ViewHolder object.

The 3 implemented adapted methods

  • now when we extend RecyclerView.Adapter<CustomAdapter.ViewHolder> we have to implement 3 methods, why do we have to do this? This is because RecyclerView.Adapter is an abstract class and when we extend an abstract class we must implement all of the abstract methods. Those three methods that we have to implement are:

1)onCreateViewHolder( ViewGroup parent, int viewType)

  • RecyclerView calls this method whenever it needs to create a new ViewHolder object. What is a ViewHolder object? In Android a ViewHolder object is used to describe an item view within the data set of the RecyclerView. This method creates and initializes a viewHolder and its associated view.
@Override
    public ViewHolder onCreateViewHolder(  ViewGroup parent, int viewType) {

        View views = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_view,parent,false);

        return new ViewHolder(views);
    }
  • Notice that on this method the return type is ViewHolder, which means that we will be returning a new instance of our static ViewHolder class. It also takes two parameters, first is the parent. The documentation states, "The ViewGroup into which the new View will be added after it is bound to an adapter position." For us I believe that the parent is the RecyclerView itself that we added to the our main Activities XML layout. The second parameter is viewType and it represents the viewType of the new View that gets created. But we do not use it so we don't have to worry about it that much. Then we have the inflating of our View.

Inflating the View

  • When we say inflating we mean the underlying Android operating system will take a XML file and turn it into its corresponding View objects. Before we can use our view to return a new ViewHolder object, we need to inflate it, How do we do this? We do so through the LayoutInflater in Android.
View views = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_view,parent,false);
  • The LayoutInflater is main class that gets used when instantiating Views. from is used to obtain the LayoutInflater from the current context. The current context is obtained from parent.getContext() which the context of the RecyclerView. context simply means the current state of the application and what resources it has access to at that current state of the Android lifecycle. Finally inflate() is used to create the View hierarchy from the specified XML resources. Which means that our view for the individual item in the RecyclerView is finally created. R.layout.recycler_view is the XML file that we created specifically for our individual unit in the RecyclerView. parent is the RecyclerView and false is to specify that we do not want to attach this view to the parent view, the RecyclerView will do that for us automatically.
  • Lastly we instantiate and return a ViewHolder object that will passed into the other adapter methods
return new ViewHolder(views);

2) onBindViewHolder(CustomAdapter.ViewHolder holder, int position)

  • RecyclerView calls this methods to associate a ViewHolder with data. It is passed the current ViewHolder object and the position in the data.
@Override
    public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position) {
        String animal = this.data.get(position);
        holder.myTextView.setText(animal);
    }
  • Notice the CustomAdapter.ViewHolder that is how we reference a nested class in Java by first mentioning its outer class. Then we get the data that corresponds to the current position in the data set, we are able to do that because our data is in the form of an ArrayList. Lastly we set the text on the TextView.
holder.myTextView.setText(animal);
  • If you are curious on where myTextView comes from look at the ViewHolder class that we created. If we want to bind data to something, we first have to reference it inside of the ViewHolder object.

2) getItemCount()

  • RecyclerView calls this method to get the size of the data set. It also uses this method to know how many ViewHolder objects to show.
@Override
    public int getItemCount() {
        return data.size();
    }
  • If you leave the default value to 0 then no ViewHolder objects will be shown on the screen.

4)Initializing the RecyclerView inside of the Activity

  • The final part in creating a RecyclerView is initializing everything in the main activity, hooking up the wires so to speak.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);// takes a layout resource file
        setContentView(R.layout.activity_main); // this inflates the UI

        //data to populate the data for recyclerView
        ArrayList<String> data=new ArrayList<>();
        for(int i = 0; i< 50;i ++){
            data.add("animal # " + i);
        }
        // Setting the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.example_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(new CustomAdapter(data));


    }
}
  • Ok so lets take this line by line. The extends AppCompatActivity is just giving us access to extra features inside of the android system. Then we come to the onCreate() method, what is onCreate()? It is a method that gets called when an activity is being created. This is where most of the initialization of an Activity is going to go. It is passed a Bundle, which is any previously saved state. We do not have any previously saved state so we don't have to worry about that. Then we have the super call super.onCreate(savedInstanceState). The super call is just to make sure the super class also gets instantiated. The setContentView(R.layout.activity_main) is what the Android system uses to inflate our XML file that is associated with this activity.
ArrayList<String> data=new ArrayList<>();
        for(int i = 0; i< 50;i ++){
            data.add("animal # " + i);
        }
  • This is us creating the data for the RecyclerView to use. All we do is create a simple ArrayList and use generics to tell the compiler we are using Strings for our ArrayList. Then we have a simple Java for loop that will run from 0 - 49 and on each iteration of the for loop it will add "animal # " + i where i is the number that represents what iteration we are on.
  • Then we can start setting up the RecyclerView.
// Setting the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.example_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(new CustomAdapter(data));
  • The first thing that we do is get a reference to the RecyclerView from its id that we set earlier. Then we set the LayoutManager, what is LayoutManager? Well it is responsible for measuring and positioning item views within a RecyclerView and for determining the policy for what item views to recycle when they are no longer visible to the user. By specifying a LinearLayout manager we create a vertical list view for our items and we pass it this which will act as the current context of the activity
  • Lastly we set our custom adapter and pass it our data we created, which is an ArrayList of strings:
recyclerView.setAdapter(new CustomAdapter(data));
  • Now run the emulator and rejoice.

-Thanks all folks!!!!

Congratulations

  • If you made it this far be sure to give your self a pat on the back, this was a long one!!!!!!

Conclusion

  • Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.
  • Also make sure to checkout my YouTube channel for more programming tutorials.

23