A few days ago I wrote an article on how to implement form validation using MVVM and data-binding. The idea here wasn’t to teach form validation, MVVM or data-binding, but rather to give a comprehensive example of how MVVM with data-binding can be used for complex requirements. I got a lot of questions from people about how can we have separation of concerns between ViewModel, Model and UI in the case of something like a RecyclerView. The short answer is that it’s possible, though it requires some thinking ahead.
Like before, while I try to provide a list of techniques to use, it’s by no means a full list. The hope is to empower you to a point where you can fit in whatever requirements you have while respecting the MVVM pattern.
That being said, our app will sport the following features:
- When the app loads, a list of dog breeds will be loaded from an API call.
- When the list is displayed we will request images for each breed as it’s loaded by the RecyclerView.
- When a dog breed is clicked, we propagate the click to the activity.
Show me the code
A demo of how to use Jetpack architecture and lifecycle component to implement a RecyclerView. The example has async…
In that code repo, you’ll find 2 implementations, each slightly different. The
master branch has an implementation that aims to have to have all functionality exposed to the Views through the ViewModel while
method_alt has an alternate implementation that in some senses simplifies the code, but the downside is that the View is aware of the Model object backing it making the code more tightly coupled and IMHO contrary to the MVVM design.
Model explained [code]
The main thing to note here is that the fetch functionality is encapsulated in the model. When we want to fetch a list of dog breeds, the model object
DogBreeds has a fetch method. When we want to fetch the images for a dog breed, the
DogBreed (singular, spelt without an “s”) has a fetch method to fetch the list of dog images.
In the instance of
DogBreeds, the model object sets the list of breeds within itself, however the
Activity is the one listening for a change to the list of dog breeds. The idea here is that the
Model will not automatically tell the
RecyclerView adapter to update itself. Instead it, a
View, such as an
Fragment attaches an observer to the dataset and tells the
ViewModel to update the adapter if needed. This is important, because we need to have a degree of separation between the
Model and the
View and if the
Model directly updates the
View, then a dependency on that particular
View is created within the
DogBreed is displayed by the
method_alt calls the fetch method for the images directly, in
RecyclerView adapter goes through the
ViewModel to tell the
DogBreed object to fetch images. I like the approach of going through the
ViewModel because under
ViewModel is meant to expose functionality in the
Model to the
View. Hence, this feels more pure to me. When an the list of images is fetched, the ViewModel stores it in an Observable map. Then the layout fetches the images from this map through data-binding. I also like this approach because the image URL’s are retained through orientation changes (thanks to the Android LifeCycle components handling this automatically) and so there is no need to fetch the images again.
CustomViewBindings explained [code]
The code here is designed to allow you to bind a
RecyclerView to an adapter without having to hold a hard reference to
RecyclerView in the
ViewModel. The main reason you don’t want to do this is that while the Adapters functionality may be reusable, you may choose to use it with a different
RecyclerView so you want to move this code out from your
ViewModel. You can see in the layouts for the
RecyclerView and the list items how these are used.
ViewModel explained [code]
As I have mentioned repeatedly, the ViewModel’s job is to expose functionality to the View and maintain some state for the View. So what you see here are the various methods that are called either from the
Fragment or from the
data-binding to get or set data.
Activity explained [code]
The main take away here is that the Activity triggers the ViewModel to fetch the list of DogBreeds and then observers for a change to the list of dog breeds. When it receives a change to the list, it asks the ViewModel to update the adapter with the new dog breeds. This is important because the Activity is the View. It knows the data it needs to display and should be listening for when that data changes or becomes available.
RecyclerView adapter [code]
This code is fairly stock standard. The only thing out of the ordinary is that the Adapter is not directly holding a reference to the data it is displaying. Instead it knows of the ViewModel and passes this down to the data-binding where the layout requests the correct object based on its index. This is slightly different from the
method_alt implementation where the
DogBreed object is directly accessed by the adapter and added to the
Hopefully there is enough there to help you use a RecyclerView with Data Binding and the MVVM pattern no matter what your requirements. One thing is for sure in order to build great Android apps, read more of my articles.