DEV Community

Cover image for Room database. RecyclerView Part 2
Tristan Elliott
Tristan Elliott

Posted on • Updated on

Room database. RecyclerView Part 2

Introduction

  • This series is going to be dedicated to the basics of Android Room library. I will be following the official google guide, HERE but I will be working in an order that makes more sense to me.

A quick word

  • This post is the continued part 2 of my last post, describing how we set up a RecyclerView to use with Room database. This post is going to be dedicated to setting up the adapter and getting our RecyclerView up and running.

The Adapter

  • The Adapter and ViewHolder(from the previous post) word together to define how our data looks.

  • ViewHolder is a wrapper around a view that contains the layout for an individual items in our dynamic list.

  • The Adapter creates the ViewHolder objects as they are needed and it also sets the data for those objects. The process of associating the objects to their data is called binding.

  • When we create a "normal" adapter we need to override 3 main methods

1) onCreateViewHolder() : RecyclerView calls this method whenever it needs to create a new ViewHolder. The method creates and initializes the ViewHolder and it's associated View, but it does not fill in the view's content. So this method is used to create a ViewHolder object but doesn't bind any data to it.

2) onBindViewHolder() : RecyclerView calls this method to associate a ViewHolder with data. The method fetches the appropriate data and uses the data to fill in the view holder's layout. This method is used to bind data to a ViewHolder object.

3) getItemCount(): RecyclerView calls this method to get the size of the data set.

  • So now lets look at the adapter we will implement.
public class WordListAdapter extends ListAdapter<Word,WordViewHolder> {

    public WordListAdapter(DiffUtil.ItemCallback<Word> diffCallback) {
        super(diffCallback);
    }

    @Override
    public WordViewHolder onCreateViewHolder( ViewGroup parent, int viewType) {
        return WordViewHolder.create(parent);
    }

    @Override
    public void onBindViewHolder( WordViewHolder holder, int position) {
        Word current = getItem(position);
        holder.bind(current.getWord());

    }

    public static class WordDiff extends DiffUtil.ItemCallback<Word> {
        @Override
        public boolean areItemsTheSame(Word oldItem,Word newItem){
            return oldItem == newItem;
        }

        @Override
        public boolean areContentsTheSame(Word oldItem,Word newItem){
            return oldItem.getWord().equals(newItem.getWord());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

What is a ListAdapter?

  • So the first thing that you noticed is that we are using a subclass of Adapter called ListAdapter. This special kind of adapter helps with updating lists. It uses Eugene W. Myer's diffing algorithm to determine what and when to update. It is very efficient.

Where is getItemCount()?

  • You probably noticed that we did not implement getItemCount(). We can do this because we are using the ListAdapter which does the implementation of this method of us.

  • If this syntax is confusing to you:
    ListAdapter<Word,WordViewHolder>
    then we need to have a little generics refresher and more specifically how to use bounded type parameters.

  • If you are already familiar with generics and bounded type parameters, then feel free to skip this next section.

Generics

  • So when we talk about generics we first have to ask ourselves, what benefits do generics give us? Well, generics have a few benefits but the main one is stronger type checking:

Type Checking : generics gives our code stronger type checks at compile time. This is every important because it erases a lot of run time errors by converting them to compile time errors. Compile time errors are much easier to identifier and fix then runtime errors.

Creating a generic class

  • A generic class is defined with the following syntax:
class name<T1,T2,T3....>{
  //Typical member fields
}
Enter fullscreen mode Exit fullscreen mode
  • The <> is called the diamond and it is what is used to hold the types. In generics the capital T is used to represent a type parameter during the declaration of a class.

  • Now if we look at the documentation of the ListAdapter we will see that it uses generics in this way:

ListAdapter<T, VH extends RecyclerView.ViewHolder>

  • As we previously mentioned, we know that T is used to represent a type during declaration, but what the heck is VH extends RecyclerView.ViewHolder? Well in order to understand that we must first learn about bounded type parameters.

Bounded Type Parameters

  • There may be times when you want to restrict that types that can be used as type parameters inside of a generic class or method. This is what bounded type parameters allow us to do.
  • To declare a bounded type parameter, we list the type parameter's name, followed by the extends keyword, followed by the upper bounds. The upper bounds is how we determine type of classes that we want to be allowed. A bounded type parameter looks like this <U extends Number>
  • This bounded type parameter is saying, only accept types of type Number and its subclasses. The type Number is what we call the upper bounds

  • Now if we applied what we just learned to the documentation of ListAdapter. We see that this:

ListAdapter<T, VH extends RecyclerView.ViewHolder>
Enter fullscreen mode Exit fullscreen mode
  • means our ListAdapter will accept any type (T) and a type of either ViewHolder or a subclass of ViewHolder . Now in our implementation we use ListAdapter like this:
ListAdapter<Word,WordViewHolder>
Enter fullscreen mode Exit fullscreen mode
  • Since WordViewHolder extends ViewHolder it is accepted by the bounded type parameter that ListAdapter uses.

public static class WordDiff extends DiffUtil.ItemCallback

  • To be able to explain this we first have to talk about DiffUtil. DiffUtil is a utility class that calculates the difference between two lists and outputs a list of updated operators that converts the first list into the second one.
  • DiffItul uses the Eugene W. Myer's diffing algorithm to calculate the minimal number of updates to convert one list to another.

What is DiffUtil.ItemCallback?

  • This is a static abstract nested class inside of DiffUtil that is used tor calculating the difference between two non-null items in the list. Then we have to implement two methods:

1) areItemsTheSame() : called to check whether two items have to same data.

2) areContentsTheSame() :called to check whether two objects represent the same item.

Implementing the RecyclerView

  • Now we have to navigate to the main activity and paste this code under the setContentView() method.
RecyclerView recyclerView = findViewById(R.id.recyclerview);
        final WordListAdapter adapter = new WordListAdapter(new WordListAdapter.WordDiff());

        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
Enter fullscreen mode Exit fullscreen mode

RecyclerView recyclerView = findViewById(R.id.recyclerview);

  • This is us getting a reference to our RecyclerView so we can later set the adapter and layout manager.

final WordListAdapter adapter = new WordListAdapter(new WordListAdapter.WordDiff());

  • This is us creating an instance of our adapter. Our ListAdapter needs a class that will be used for the diffing algorithm, that is why we pass in WordListAdapter.WordDiff().

recyclerView.setAdapter(adapter);

  • setAdapter(adapter) is how we set the adapter on our instance of the RecyclerView.

recyclerView.setLayoutManager(new LinearLayoutManager(this));

  • This is needed to set the layout manager and for us we are using a LinearLayoutManager. this is used as the context which LinearLayoutManager needs. When dealing with activities this represents the activity context. A LinearLayoutManager will display our data in a linear fashion.

Starting the application

  • The last thing that we have to do it to run our application to make sure everything is working. For now all we have is a blank screen. As long as the app doesn't crash we can consider the code working.

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.

Top comments (0)