Multi-Type Recycler View: A Better Approach
Working with Recycler Views is a very common task for Android Developers. You often find yourself designing rows and writing adapters to make your list look better. Doing this very often can make us write code that looks ugly. If you find yourself writing several if-else cases in adapters, trust me something is wrong.
Here we look closer and try to write as clean code as possible, by separating Models, Views and Presenters.
The very first question, What is Model, View, Presenter or MVP Architecture?
- Model: This is the data access layer such as database API or remote server API.
- View: This is the layer that presents the data and interacts to the user.
- Presenter: This is the layer which provides View with data from Model. It also handles any background task.
Now, we will look at some code blocks that clearly states our Models, Views and Presenters.
Above is the adapter class, which contains four types of views. Following the MVP architecture, we write separate ViewHolders for different types of views. Now we also need a Base class to construct the various ViewHolders depending upon the type.
Let’s have look at our BaseViewHolder class:
Our BaseViewHolder class is a simple generic class that takes only two params, one is ViewGroup and the other is ResourceId (int), to construct the respective view. We also have an abstract function named setData() which takes a generic datatype as its param. The soul function of this is to set the data for the respective view.
We have our Views ready, now we can create our Presenter layer:
Above code shows our Presenter layer, which takes data as a param, and breaks it down into individual items and populate the itemsList accordingly. The data here comes from a remote server API, however you can populate your list as per your requirements. Here the convert() function does the trick for us and returns a list of items of different types.
Let’s have a look at our onBindViewHolder() function again:
This function simply gets the item from our itemsList and calls the setData() function of our BaseViewHolder class. Now the question is, how do the respective views get their data? The answer lies in the code block below:
Remember we created our setData() function abstract. Here is why it is useful, when you write your ViewHolder class, you need to implement the base class (BaseViewHolder) methods. What do we have now? We have our View, we have our data, its time to populate the views with the respective data. That’s it! No unnecessary if-else blocks, no ugly code, and above all what we get is a better adapter class.
Let’s have a quick look over the problems we tackled:
Problem 1: What if we have a single ViewHolder class?
Every time we need to check which ViewHolder is returned in onBindViewHolder(), which makes us write unnecessary if-else block.
Problem 2: What if have multiple adapters of different data types?
The basic concept behind this is to make the BaseViewHolder class generic, something like BaseViewHolder<T>. We must create a single BaseViewHolder such that it can be used by different adapters.
Problem 3: What is the real advantage of making separate ViewHolders?
Every time in our onCreateViewHolder(), we inflate our views and our onBindViewHolder() is intelligent enough to return us the right view at the right position, we just need to pass data to it and the rest is magic!
A quote from a book named Clean Code: “ Even bad code can function. But if code isn’t clean, it can bring a development organization to its knees.”
You can get the full project here.