1. 程式人生 > >Android Data Binding + ListAdapter

Android Data Binding + ListAdapter

Data Binding is a pretty powerful library that I've shamefully ignored for most of my time as an Android dev (most likely because it used to be a little contradictory). I've been reading a lot about it, and I naturally stumbled upon George Mount's blogposts. The article that really caught my attention was this one about Data Binding on a RecyclerView

:

That was the moment I realized how great Data Binding can be, and being an avid user of , I thought things could get even better if I added it to the mix. This will be a brief blogpost about how we can make the best out of Data Binding and ListAdapter in order to reduce boilerplate and just make our code simpler and prettier.

Let's get started

… but first, just in case you don't know what a ListAdapter is:

RecyclerView.Adapter base class for presenting List data in a RecyclerView, including computing diffs between Lists on a background thread.

It's a base Adapter that automatically updates itself (with free animations!) when the list changes. Calling notifyDataSetChanged()

and its friends is now a thing from the past — all you need now is to call submitList() passing the updated list. If this is new to you, it might be a good idea to take some time and learn more about it — the documentation is pretty good and might actually be the only resource you'll need.

Now back to the important stuff.

The main idea of the blogpost I just mentioned above is how we can come up with a generic ViewHolder that can be reused for any Adapter in our app. It assumes the ViewHolder will only need a single data object for the binding (which is definitely a good practice anyway). This object is the parameter of the bind() method, and all we do with it is set it as the variable for a we receive as a constructor argument. Through a clever naming convention trick we'll be able to make this ViewHolder work wherever we want (more on that later):

Yes, this tiny class has the potential to replace most ViewHolders you've written so far. The original code fulfils our purpose here, so we won't change a thing. If you want to understand that executePendingBindings() line, head out to the original post for a great explanation.

Next, we can create a base Adapter that will have most of the boilerplate we'll need for any Adapter we'll write. Our DataBindingAdapter will be a little different here because it'll extend ListAdapter:

That's it, a super simple class — even simpler than the original thanks to the ListAdapter. There's an important assumption we make there: the layout id used by the Adapter to inflate the layout is the viewType, so any child Adapter will need to override to indicate which layout should be inflated. This assumption is another good practice that is even reinforced in the documentation.

Consider using id resources to uniquely identify item view types.

Now let's say we want to show a list of books. Given those two classes we just wrote, let's see how our BooksAdapter would look like:

Thanks to our base classes, the only things we'll need to worry about when creating an adapter is writing the required by the ListAdapter and indicating the layout id we want to inflate through the getItemViewType() method. Of course we also need special attention in our layout file:

The important thing here is that the variable name must match whatever name we're using in the DataBindingViewHolder — in this case it's item. The rest is simple and standard Data Binding code.

What about Adapters with different ViewHolders?

Well, I'm glad you asked. Usually, the only thing we'll have to change is how we're implementing getItemViewType() in the Adapter, but normally the DiffUtil.ItemCallback will have to change too.

Let's say our list of books will have sections, which will naturally be represented by a completely different layout file. We'll make sure our Book model and our new Section model share an interface, let's say Listable, just so we can have them on a same list. Sealed classes would also be a nice option here, but let's not get into that.

This is how our BooksAdapter would look like:

And there's nothing special when we want to use this adapter in an Activity, for instance: