Android Adapters — A different approach
Building a list of items on Android is very simple. We set up a RecyclerView, bind an adapter, throw in some items with their ViewHolders and magic happens.
Everything works and we’re all happy. Well… as you can imagine… I’m not happy at all :D and that’s the reason why I wrote this post.
The Monster adapter
The following code is an example of a working list adapter. It supports three types of items: a header with a configurable label, a simple contact item that displays name and email and some dummy object with two labels.
I think that all Android developers made something like this at least once and it’s the kind of example that I see all over the place in tutorials. I would like to highlight some problems with this approach:
- The adapter should be used only to display items in a list. It shouldn’t have to know anything about how to render a specific item. This is being done on the bindSeparator, bindContact and bindDummy methods. It definitely shouldn’t be here.
- The logic of loading the xml layout to inject it in a ViewHolder is in the adapter. Again this isn’t the Adapter responsibility.
- The ViewHolders are bounded to the adapter. It can be improved to allow reusability.
A simple way of fixing this is by splitting the adapter in smaller components and delegate responsibility. So my approach is to create three base components: BaseAdapter, ViewHolderManager and BaseViewHolder.
The BaseAdapter is responsible for receiving a list of items and displaying them. It uses a ViewHolderManager for building the item specific ViewHolder. This is where I apply the xml layout selection logic.
The ViewHolder is able to render itself. It includes assigning click listeners to holder elements, setting data on textviews, fetching images with Picasso/Glide, etc.
So let’s see some code.
BaseViewHolder — with great power comes great responsibility
First, I removed the ViewHolders from the adapter class since I didn’t want to bind them to any specific adapter.
I started by creating an abstract BaseViewHolder which is responsible for binding the click listener. More common functionality can be placed in this class. Every ViewHolder extending it must implement it’s own render logic, including view elements binding (you can use ButterKnife) and setting data from model.
ViewHolderManager — The new sheriff in town
Now the ViewHolderManager. It’s an interface that defines all methods needed to build an item ViewHolder and bind the xml layout to it. I use a different manager for each adapter.
The class MultiItemViewHolderManager is a ViewHolderManager implementation that evaluates the ViewType and inflates the corresponding xml layout. It then returns a fully loaded specific ViewHolder.
For simplicity, in this example the choice depends only on a ViewHolder type that is evaluated. Normally I use more complex logic divided through other components.
BaseAdapter — I know nothing
Finally the adapter. The BaseAdapter implements all common methods using generics. It delegates the view build logic to the ViewHolderManager and the bind data logic to the ViewHolder itself.
The class MultiItemAdapter is an example of an adapter that allows multiple item types and supports click events. Much simpler than the typical monster adapter.
Do you have a working sample code?
Yes I do :D You can check a full example of this code here. There’s an example of a Monster adapter, a simple adapter with only one item type and a multi item type adapter.
My next post will be about how to inject dependencies into the adapter (using Dagger2), adding swipe to dismiss and reusing ViewHolders as item templates.
And that’s all, thank you for reading.