Data Tags, Binding Expressions, Imports and Include

Oya Canlı
Enpassio
Published in
7 min readJun 18, 2019

This is the second part of a series of articles on Android Data Binding Library. Here are the others:

  1. Introduction to Data Binding
  2. Data Tags, Binding Expressions, Imports and Include(You’re here)
  3. RecyclerViews and Event Handling with Data Binding
  4. Binding Adapters and Observable Objects
  5. Two-Way Data Binding

And here is the github repository with several data binding samples(both in Kotlin and Java) as a companion to these articles.

In the previous article, we have seen how to set up data binding in our project. In this second article, we will see binding data from xml using <data> tags, binding expressions and use of imports in xml. We will also see what to do when we use <include> tags.

Bind data to your views

The capabilities of Android Data Binding Library are not limited to view binding. You can also pass data to the binding implementation. You can declare how the data will be shown in your layout from your xml. This way, you won’t need to set data to each of your views programmatically.

For passing data to your binding implementation, you need to declare some variables in your layout. These variables have to be declared within <data> </data> tags, as follows:

So, these data tags will be the first child of your <layout> tags and your root viewgroup will be the second child.

Variables are declared using variable tags and each of them must have a name and a type. The library will use this information and generate a variable of the given name and type in the binding class, as well as a getter and a setter for that variable. Getters and setters will be generated according to the naming conventions. So for the variable named “product”, the getter will be named getProduct and the setter will be named setProduct.

You can use variables of any type according to your needs: sometimes you might need to pass a primitive variable like a boolean, sometimes you might need to pass a custom object and sometimes you might prefer to pass a viewModel.

Once you declare a variable inside the data tags, you can use that variable and its fields inside your xml file, to populate your views with data.

So the data should be put inside “@{ }” notation.

When you declare it like this in your xml, under the hood, Data Binding Library will be using the setText() method of the TextView class and will be passing the productName property of the product object as an argument to it. In fact, all the views (with ids) and variables you declare in the layout, will become fields of the generated binding class. So you can have access to these fields through the binding instance. (We had seen previously how to get an instance of the generated binding class.)

When you want to populate the UI with data, all you need to do is to pass the data by using the generated setters for your variables:

//Kotlin
binding.product = product
//Java
binding.setProduct(product);

There is another way of doing this as well:

//Kotlin & Java
binding.setVariable(BR.product, product)

This generic method is from the ViewDataBinding class, which is the parent of all generated data binding classes. BR is a class generated by the library which contains all the variables you used with data binding. But note that this method is not type-safe and it doesn’t check whether the mentioned BR variable is indeed located in the specific binding class. That’s why I would recommend using the specific setters instead. However, if the specific type of the binding class is not known at compile time (for instance, if you’re writing a base class for your activities/fragments) you can use this generic method instead.

When you declare the data in xml layout with data binding, instead of setting it programmatically, you don’t need to access the views one by one from your Java/Kotlin code. So, instead of setting data one by one to widgets like this:

//Kotlin
binding.detailsProductName.text = product.productName
binding.detailsProductPrice.text = product.productPrice
...
//Java
binding.detailsProductName.setText(product.productName);
binding.detailsProductPrice.setText(product.productPrice);

You can pass it at once like this:

//Kotlin
binding.product = product
//Java
binding.setProduct(product);

In that case, since you won’t need access to these views from your code, you don’t need to specify ids for those views. If you check out this layout from our first sample, you’ll see that textviews and the imageview don’t have view ids. So if you need access to a specific view programmatically for some reason, then you’d need to give it a view id, so that you can find that view from your Java/Kotlin code using its id. Otherwise you don’t need it. (Though, of course, if you’re using constraint layout or relative layout, you still need view ids for relative positioning of views.)

When you set a variable from your code, changes in the binding implementation are scheduled to be executed before the next frame. (Frames change approximately each 16ms, which is pretty frequent anyway) However, sometimes you might need the changes to be executed right away(for instance, the code following that might depend on these changes) In such a case, you can call:

//Kotlin & Java
binding.executePendingBindings()

This will force the changes to be executed right away.

E. Imports and Binding Expressions:

While declaring data from xml layouts, you can make use of some helper methods:

You can also make use of your own helper methods. For convenience, you can put your helper methods in a utility class, then import that class and use it whenever you need inside the layout. Import statements are declared within <data> tags using the tag import

Here, the imported class BindingUtils contains a helper method addCurrencySign() which adds a currency sign to the price. Note that you could also have access to the method by giving the full path inline, instead of importing the class, like:

android:text="@{com.enpassio.databindingwithrecyclerview.utils.BindingUtils.addCurrencySign(product.productPrice)}"

However, the first approach is more readable.

You can also use mathematical, logical, binary operators, ternary operator and even more in xml layouts. Checkout the following example:

Here, the visibility of the RecyclerView is determined by the boolean isEmpty, which is kept in the viewModel. If isEmpty in the viewModel is true, RecyclerView won’t show up, otherwise it will become visible. Notice that we need to import View class, in order to have access to View.VISIBLE or View.GONE fields. As you see, we can also declare a default state.

There is also a null-coalescing operator “??” which simplifies the following common case scenario:

android:text = "@{product.productName != null ? product.productName : @string/unknown_product}"

So instead of this long ternary operator, you can use null-coalescing operator as follows:

android:text = "@{product.productName ?? @string/app_name}"

Notice that you can also use resources in binding expressions. Suppose you have a string resource with a string variable like that:

<string name=”welcome_message”>Welcome back %1$s!</string>

You can use that string resource within a binding expression as follows:

android:text = "@{@string/welcome_message(user.name)}"

For accessing members of a list or map, you can use “[ ]” operators.

android:text = "@{list[index]}"
android:text = "@{map[key]}"

Some characters need to be escaped in XML: for instance, if you want to use “&&” logical operator, you need to write it as “&amp;&amp”;

Although you can write relatively complex expressions in XML using a combination of operators, note that it is not advised to do so, because it can be error prone and you can’t unit-test the code in xml. Besides, it would be difficult to read. So, it is advised to use expression language only for simple statements. If you need a more complex operation/statement, you’d better create a helper method or a binding adapter.

For more information on binding expressions, you can check out the official guide.

F. Included layouts

In Android, sometimes we use <include> tags for reusing a piece of layout. When you use data binding in such a layout, you need to add a few steps.

First of all, both parent layout and the included layout should be wrapped in <layout> tags. You should also make sure you add an id to your <include> tag. The library will generate two classes for these two layouts. The binding class of the parent layout will have a reference to the binding class of the included layout and it will use the id you have given as the name of that reference. (If all these sound confusing, you can always take a look at the generated binding classes in your build folder. They are human-readable and will help you understand the underlying logic. Though, you don’t really need to know all these details to successfully use data binding in your projects.)

Suppose you gave the id “included_layout” to your include tag. For reaching the views inside the included layout, you can call:

binding.includedLayout.recyclerView

For passing data to the included layout, you have some options. If you need this data only in the included layout, then you can declare the variables only in the included layout and you can set them in a similar fashion:

//Kotlin
binding.includedLayout.viewModel= viewModel
//Java
binding.includedLayout.setViewModel(viewModel);

If you need your variables in both the parent layout components and in the included layout, you can declare these variables in both layouts. Then you will set the variable to the parent layout and by adding a line to your include tag, it will automatically pass the variable to the included layout.

Suppose you declared a variable called “viewModel” in both parent and included layouts. You’ll pass your variable(viewModel) to the parent layout as usual:

//Kotlin
binding.viewModel = viewModel
//Java
binding.setViewModel(viewModel);

Then you will add a line to your include tag as follows:

The attribute “bind:viewModel” is formed by the name of the variable “viewModel” and the “bind” namespace. This will pass the variable automatically to the included layout.

In the next article, we will see how to set event listeners with data binding and how to implement data binding in RecyclerView item layouts.

--

--