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());
}
}
}
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 usesEugene 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 usebounded 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
}
The
<>
is called thediamond
and it is what is used to hold the types. In generics the capitalT
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 isVH extends RecyclerView.ViewHolder
? Well in order to understand that we must first learn aboutbounded 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>
- 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>
- 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));
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 activitiesthis
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)