In the previous post, we went over implementing permission request flow for an Image Analyzer app. In this post, we’ll cover how we might apply MVVM to load photos into RecyclerView
:
Plan of Attack
As mentioned in the previous post, we want to avoid fighting the platform. Instead, we will try to find a good balance of utilizing Android components and ViewModel
. To load the photos in RecyclerView
, we’ll to the following:
- Use
CursorLoader
to load thumbnails fromMediaStore
- Load the data into
RecyclerView.Adapter
- Bind a
ViewModel
to each itemView
- Set the data in
ViewModel
inRecyclerView.ViewHolder
Loading Thumbnails
When READ_EXTERNAL_STORAGE
is acquired, GalleryViewModel
will send LoadPhotosEvent
via the EventBus
to GalleryActivity
. At this point, we can use LoaderManager
to load the thumbnail data, and pass it to the ThumbnailAdapter
. The relevant code is highlighted in the snippet below:
In ThumbnailAdapter
, let’s take the Cursor
as is and use it:
Item Layout and ViewModel
Now that we have the cursor loaded, we want to do the following:
- Set a square thumbnail layout in the item view
- Load the image using Glide
- When the image is clicked, send out an event to view the image
We’ll use a ConstraintLayout to create the square ImageView
, use custom binding to load the image via Glide, and use EventBus
to handle image click.
The corresponding XML layout is below:
The custom binding to handle image loading via Glide:
And finally, ThumbnailViewModel
:
The ViewImageEvent
above has some extra data that the next Activity
needs to load the full image and to create a Scene Transition Animation.
Creating and Binding ViewHolder
We now have the Adapter
in place to load and set the data, and a ViewModel
to load and represent the UI. The next step is to set up onCreateViewHolder
and onBindViewHolder
to connect the Adapter
and the ViewModel
. This is pretty straight forward.
In ThumbnailViewHolder
, whenever the data is changed, update the ViewModel
data and execute the binding:
And in ThumbnailAdapter
, set the ViewModel
and the Binding
in onCreateViewHolder
, and set the item data in onBindViewHolder
:
Testing
We have now separated the responsibilities as follows:
GalleryActivity
handles loading of theCursor
ThumbnailAdapter
handles reading theCursor
and setting the current item data on theViewHolder
ThumbnailViewModel
handle theView
representation and the image click
This makes the ThumbnailViewModel
very testable:
In this example, we’ve used a simple RecyclerView
with only a single element type. In a typical production app, there may be many different types of items in a single RecyclerView
, resulting in multiple ViewHolders
and ViewModels
. With this approach, all of these individual items in the RecyclerView
can be unit tested and even reused.
We’ll go over making the network request to analyze the image in the next post.
Thank you for reading! Feel free to say hi or share your thoughts on Twitter @hiBrianLee or in the responses below!
Full project source code: https://github.com/hiBrianLee/ImageAnalyzer
Other parts of this post:
- Pragmatic Approach to Android MVVM: Part 1
- Pragmatic Approach to Android MVVM: Part 2
- Pragmatic Approach to Android MVVM: Part 3
Some of my other posts: