Data Binding Cookbook: tasty recipes for more logic in your code

Björn Kopiske
ProAndroidDev
Published in
11 min readOct 10, 2020

--

Photo by Katie Smith on Unsplash

Data Binding offers a broad range of ways to strip UI actions from your Fragment’s code. In this article series I’ll give you practical examples on using Data Binding in a variety of scenarios.

Although Google’s documentation is a good reference for working with Data Binding, difficulties will pop up when working with it. This cookbook shall help you overcome them easily!

I’ll start with the most basic topics and then raise the bar — step by step. This first article will cover the most basic aspects whereas the the next articles will dive deeper.

Data Binding vs. View Binding

Before diving into Data Binding, let me first explain what it is not: Data Binding is not View Binding! Whereas View Binding solely supports you getting rid of using findViewById calls by providing null- and type-safe access to a view’s controls, Data Binding provides you with a rich set of ways to move UI related code into the respective layout’s xml.

First of all, one can as well avoid using findViewById by using Data Binding: you will no longer reference controls in the code but the layout’s xml. But it’s a totally different approach to get rid of findViewById. Note, that when using Data Binding, you get View Binding tooling automatically.

To better grasp that difference, let me show you a quick example — I’ll go into more detail in the Getting started section.

View Binding frees you from using findViewById:

As you can see above, View Binding leaves the necessity for Fragment code unchanged. Apparently, you do not make any changes to the layout’s xml.

Now, let’s move on to Data Binding:

Data Binding allows doing the same thing without any code in the Fragment — it’s a declarative approach, configured in the layout’s xml! Particularly you do not need any calls to findViewById anymore.

Assigning data from the ViewModel to a view’s property is a common task for Data Binding, but you can do a lot more with it — this is what this article series is about!

When studying the code above you have probably wondered how View and Data Binding’s magic works. Let me, for now, just say that a kapt processor takes care of generating code which provides that magic. When talking about debugging Data Binding in one of the next articles in this series I’ll get back on this topic.

Getting started with Data Binding

I’ll give you examples in a cookbook style, i.e. I’ll present the problem to solve — sometimes even the code before — and give you a recipe which you can apply to this problem. You’ll then see the code after migration to Data Binding.

Start without Data Binding

First, acquaint yourself with the app I’ve prepared as starting point to showcase Data Binding: clone the repo and check it out at GitHub!

It’s a simple app consisting of an Activity embedding a single Fragment via a NavHost using the Jetpack Navigation component. It just displays the date and time on launch of the app. It does not make use of View nor Data Binding.

As you can see below, the Fragment asks the ViewModel for the current timestamp after view creation and feeds it into a TextView:

Adding Data Binding

To use Data Binding, it has to be enabled in the build.gradle first:

After a Gradle sync, Android Studio knows about the availability of Data Binding — one can start using Data Binding!

To be able to use Data Binding in a Fragment, the layout’s xml has be be wrapped in a layout container:

Android Studio offers the context action named Convert to data binding layout for this. Note that the empty <data/> tag is created.

Next, the layout has to be inflated using DataBindingUtils to allow Data Binding to assist:

Now, look at the changes to the Fragment after using DataBindingUtils for inflation:

You can find the changes so far in this branch.

But is this using Data Binding? — No, as initially discussed, it’s just using View Binding! With Data Binding we can completely remove the onViewCreated override and move our code to the layout xml:

First of all, the <data/> tag now contains a variable definition for the ViewModel at lines 6–8. Furthermore the ViewModel is referenced for the TextView’s text property at line 21. — Using variables is the most common way to access data in the layout’s xml. There are other ways, but for simplicity I’ll show them later.

But when running the app now, the TextView does not receive any data! The reason is simple: Data Binding has no source for the viewModel variable, it has to be set programmatically at line 6:

Note that the app hasn’t crashed but just ignored the unassigned ViewModel. Although this behavior masks errors, it also prevents your app from accidental crashes.

After adding the ViewModel assignment, the app works as expected without any UI related assignments in the Fragment’s code. That’s what Data Binding is about!

Photo by Markus Spiske on Unsplash

Last but not least don’t forget to clear the binding reference to avoid leaks:

Have a look at the full migrated, working code here!

Handling changing data

Currently, the app does not update the displayed timestamp — it will always display the startup time of the View.

Imagine, the app without Data Binding were to implement periodic updates of the timestamp. The changes could look like this:

Note, that the ViewModel now provides a LiveData<String> instead of a simple String as return value. The Fragment observes on any changes of the LiveData value.

This can be tedious and bloat the Fragment’s implementation. When using Data Binding instead we have to do nothing. One can simply rely on Data Binding to update the UI whenever the LiveData’s value changes! — Well, yes, Data Binding still needs means to observe LiveData changes, so the lifeCycleOwner has to be set once, as you can see at line 6 — but that’s all:

Make sure to use viewLifeCycleOwner for dealing with potential detaching of the Fragment and avoiding memory leaks. If you’re working with an Activity here, using this instead is fine, as stated here.

So, the Fragment still does not require any UI update code, everything is done within the layout’s xml!

Go, grab the code again and play around with it!

Using text resources

The simple way

You may have noticed that the previous implementation of Data Binding left out using the resource R.string.timestamp. I left out that part to focus on the important things — but let’s catch up with that now:

As you can see, only a slight change to the layout’s xml was necessary to re-integrate using the string resource. — You find the full code here.

Plurals

If you need to differentiate text display by item count, you can make use of plurals resource strings.

To demonstrate that we’re going to modify our app to still show the timestamp, but also the number of timestamps, i.e. dateEmitCount, which have been shown so far.

Let’s modify the ViewModel to expose dateEmitCount:

Note, that — besides using a randomized delay — the functions delivering LiveData have been replaced by member variables.

Why? Can you guess what would happen, if you called the old getDate() twice? — Exactly, you’d get another instance of a LiveData which is independent of the first one. In our previous sample this wasn’t a problem since the emitted data was stateless resp. just depending on the system time.

But in the code above, we have two LiveData instances which depend on each other. This is no problem by itself, but remember, we’re talking about Data Binding, i.e. there will be another LiveData consumer: the layout’s xml:

Data Binding should not get a second instance of the LiveData provided by the ViewModel because of the LiveData chaining:

The delay time is randomized, which leads to different wait times for different LiveData instances of the date variable. In consequence, dateEmitCount will emit its value in a different timing for different instances. Thus, Data Binding probably were to receive a different number of emitted timestamps compared to the number of actually emitted timestamps.

We avoid that by creating the LiveData instances just once!

But let’s focus on Data Binding: as you can see, we reference this LiveData variable instead of calling the method which returned a new LiveData instance at line 3.

In the same way, we can access the dateEmitCount LiveData in line 8. Here you see how to actually work with plurals: using @plurals instead of @string and passing two instead of one number argument.

But what are these arguments? The first one specifies the number on which to select the proper text (i.e. singular/plural) wheras the second one specifies the number to fill into the text’s placeholder.

If you wondered where these texts are defined, have a look at this xml values file:

Again, no changes in the Fragment were necessary, just in the layout’s xml (and of course the ViewModel in order to provide new data).

Of course, you can check out the code again!

Finally a word of warning when using plurals: you will probably face issues when specifying quantity for zero: it will not be used for the number zero, but the text for quantity other will be used. This is not a bug, but a feature — in English language the plural form is used when speaking about zero items.

Handling user interaction

Data Binding can not only help to present data, but also to react on user interactions.

To showcase that let’s modify our app to provide a button which resets the number of emitted timestamps to zero.

Since the ViewModel manages counting the number of emitted timestamps, we provide means to reset the counter there:

Next, we add a button to the layout’s xml:

Before making use of Data Binding for calling the reset method, let’s see how changes to our Fragment would look like without Data Binding:

So, again we’ve to override a method in the Fragment to attach the individual onClick handler programmatically. Let’s get that idea immediately out of our heads and see how easy this can be achieved using Data Binding by adding just the onClick handler to the layout’s xml:

That’ s all we have to do — no nasty changes to the Fragment! If you want to play around with the code, get it here!

Passing arguments on method invocation

The reset method we’ve already introduced does not require any arguments. It can be handy to provide arguments, e.g. for information the ViewModel itself does not provide: imagine you want to show a snackbar on button tap — how do you determine the anchor the snackbar should be relative to?

Of course, you can specify the anchor within the Fragment where the snackbar displaying takes places, but this prevents you from having control over the anchor within the layout — this doesn’t play nice with Data Binding principles.

Here, passing arguments via Data Binding comes to the rescue! Let’ see what infrastructure we’ve to provide for proper snackbar displaying.

Say, we want to specify the message to display and, of course, the anchor directly in the layout’s xml:

You see, no real surprises here — the message and the anchor are just passed as parameters. One thing to mention is the naming convention: buttonSnackbar is derived from the id button_snackbar under the hood.

But how does the ViewModel actually display the snackbar? Somehow it has to interact with the UI components. Luckily, the Google Developers have come up with the SingleLiveEvent pattern for this, which we implement here:

You can find the full code here.

I really recommend to read Google’s blog post on the pattern, but in short: we’ve a LiveData in the ViewModel which holds an Event with all required data to show the snackbar, the Fragment observes the Event and displays the intended snackbar. Furthermore, the Event can only be consumed once. — In case you’re missing the Event class, check out that blog post!

Well, you might complain about having a lot of code here, even including Fragment code, to display the snackbar since you could have simply extended the Fragment with an onClick listener showing the snackbar. Although this is a valid objection, it is only applicable to such simple examples:

In a larger application, you will create a base class for your Fragments which takes care of observing and displaying snackbars. Also, you create a base class for your ViewModels which provides the counter part.

Then, without any changes to either your Fragment or ViewModel you can just call the ViewModel’s showSnackbar method to display a snackbar purely from the layout’s xml — where ever you want!

We’ll see another example for arguments with RecyclerViews, but I’ll cover them in another article!

Usage of variables

In the previous examples we’ve always used LiveData as a data source, although it is not a requirement to work with LiveData for Data Binding. You can use any data type in the ViewModel:

Using the static variant as seen at line 1 will help us only in rare cases where the data provided from the ViewModel does not depend on other data and won’t change.

Although possible, I don’t recommend using ObservableField since in times of MVVM architecture one should let the framework help with lifecycles, i.e. one really should use LiveData. — It will free your mind from manually dealing with lifecycles!

Note that access to the data variable will be the same in all three variants. Data Binding takes care of unboxing LiveData or ObservableField transparently — but make sure to set lifeCycleOwner for unboxing LiveData!

Furthermore you cannot just use the ViewModel as a data source, but also declare all kinds of variables for Data Binding in the layout’s xml:

You can access these variables in the same way as already seen in the other examples.

Regardless of what you can do, try to minimize the variables you provide to the layout. It’s still UI and should not contain application logic.

Restrictions

So, Data Binding is the one-size-fits-all-solution for handling data display in the UI and reacting on user interaction? Unfortunately not!

As you may have guessed when I talked about the requirement of a layout container for Data Binding to work, it can only be used for working with layouts!

Thus there’s no way to get it working with older Android framework components, e.g. PopupMenu!

Expression language

If you have wondered about the cryptic @{…} syntax in the layout’s xml and are confused, have a look at the Data Binding expression language. It’s a language on its own and has its own restrictions. Anyway, one get’s quickly accustomed to it.

In this article series you’ll see a lot of examples which will help learning to solve commons problems with the expression language.

Closing thoughts

We’ve seen the basic concepts of Data Binding, i.e. how Data Binding complements View Binding, how you initially set up Data Binding, how to display static as well as changing ViewModel data in the UI without touching the Fragment, how to work with text resources, how to handle user interaction and how to pass arguments using Data Binding.

Furthermore we’ve briefly considered usage patterns of variables in layouts and restrictions where Data Binding can be used.

Stay tuned for the next article in this series where I’ll show you more advanced recipes, e.g. for two-way binding!

--

--