Android Data Binding: RecyclerView

Reduce, Reuse, Rebind

George Mount
Android Developers
Published in
4 min readJan 10, 2017

--

While sometimes I like to think so, Data Binding as a term doesn’t always mean Android Data Binding. RecyclerView has its own way of binding data to the UI. RecyclerView has an Adapter with two very important methods that we implement to bind data:

RecyclerView exposes the common ViewHolder pattern as a first class citizen in its API. In onCreateViewHolder(), the Views are created and the ViewHolder contains references to them so that the data can be set quickly. Then in onBindView(), the specific data is assigned to the Views.

Android Data Binding in RecyclerView

As discussed in a previous article, Android Data Binding can be treated like the ViewHolder pattern. Ideally, we’d just return the generated Binding class from our onCreateViewHolder(), but it doesn’t extend RecyclerView.ViewHolder. So, the binding class will have to be contained by the ViewHolder instead.

Now, my adapter can create and bind using Android Data Binding:

If you were looking closely, you saw the executePendingBindings() at the end of MyViewHolder.bind(). This forces the bindings to run immediately instead of delaying them until the next frame. RecyclerView will measure the view immediately after onBindViewHolder. If the wrong data is in the views because the binding is waiting until the next frame, it will be measured improperly. The executePendingBindings() is important!

Reusing the ViewHolder

If you’ve ever used a RecyclerView’s ViewHolder before, you can see that we’ve saved a bunch of boilerplate code in which the data is set into the Views. Unfortunately, we still have to write a bunch of ViewHolders for different RecyclerViews. It also isn’t clear how you’d extend this should you have multiple view types. We can fix these problems.

It is common to have only one data object passed into a data binding class, like item above. When you have this pattern, you can use naming convention to make a single ViewHolder for all RecyclerViews and all view types. The convention we’ll use is to name the one view model object “obj.” You may prefer “item” or “data,” but if I use “obj,” it is easier to pick out in the example.

In MyViewHolder, I am using ViewDataBinding, the base class for all generated bindings, instead of the specific ItemBinding. This way, I can support any layout in my ViewHolder. I’m also using setVariable() instead of the type-safe, but class-specific, setObj() method so that I can assign whatever view model object type that I need. The important part is that the variable must be named “obj” because I use BR.obj as the key in setVariable(). That means you must have a variable tag in your layout file like this:

Of course, your variable will have whatever type your data bound layout requires instead of “Item.”

I can then create a base class that can be used for all of my RecyclerView Adapters.

In this Adapter, the layout ID is being used as the view type so that it is easier to inflate the right binding. This lets the Adapter handle any number of layouts, but the most common usage is to have a RecyclerView with a single layout, so we can make a base class for that:

What’s Left?

All the boilerplate from the RecyclerView is now handled and all you have left to do is the hard part: loading data off the UI thread, notifying the adapter when there is a data change, etc. Android Data Binding only reduces the boring part.

You can also extend this technique to support multiple variables. It is common to supply an event handler object to handle things like click events and you may want to pass that along with a view model class. If you always pass in the Activity or Fragment, you could add those variables. As long as you use consistent naming, you can use this technique with all of your RecyclerViews.

Using Android Data Binding with RecyclerViews is easy and significantly reduces boilerplate code. Maybe your application will only need one ViewHolder and you’ll never need to write onCreateViewHolder() or onBindViewHolder() again!

--

--