RecyclerViews and Event Handling with Data Binding

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

This is the third 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
  3. RecyclerViews and Event Handling with Data Binding(You’re here)
  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 part, we have seen how to use data tags, binding expressions and imports. In this third article, we’ll see how to use data binding for recyclerview item layouts and how to set event listeners from xml.

Binding in a RecyclerView

Just like we bind views and data in our activity and fragment layouts, we can do the same for RecyclerView item layouts as well. This way, we’ll get rid of the calls to “findViewById” in our viewholders and we’ll have less code in the adapter, which can easily be reused with small modifications.

To do that, we’ll need to implement the same principles in the xml layout for items: we’ll wrap the layout inside <layout></layout> tags, add <data></data> tags and add our variables.

Typically, when we have a RecyclerView, we use a POJO(plain old java object) or data class in Kotlin, for representing each item. So, it would make sense to pass an instance of this class to the item layout and populate the layout with its fields.

As you know, item layouts are inflated by the adapter: onCreateViewHolder method is the place where item layouts are inflated and onBindViewHolder method is the place where data is bound to the views in the item layout. Custom ViewHolder typically contains the views in the item layout as properties and these view objects are typically initialized by findViewById.

When we use data binding instead, we don’t need any of those view properties and findViewByIds in the viewholder. The only property we need is the binding instance:

Click for Java version.

Inside the onCreateViewHolder method, we’ll create a binding instance while inflating the item layout:

Click for Java version

And inside the onBindViewHolder method, we’ll set the corresponding data to the binding instance:

Click for Java version

As I mentioned in the previous article “binding.executePendingBindings()” is for forcing pending changes to apply right away. If you try the code without this line or if you ever forget it, you may see that it works anyway! However, this can occasionally cause bugs, so make sure to call executePendingBindings anyway.

We can further improve the adapter code by encapsulating some of this code inside the custom viewholder and simply call them from the adapter. Checkout the improved version of the adapter code here: Kotlin / Java

You can recognize that adapter has clearly less boilerplate code than it would without data binding. And the code of the adapter will be mostly similar for all projects, so you can reuse it with small modifications.

Note that you don’t have to change anything in the layout where RecyclerView widget itself is located. You may or may not use data binding in that layout: they are different layouts and they are not dependent. We used data binding in that layout as well, however, it is not obligatory.

We can also add an item click listener to the RecyclerView, but we’ll see that in the next section.

Event Handling

We can also set event listeners (click events, touch events) from xml using data binding. There are several ways of achieving this.

  1. One way is to specify listener objects. For instance, you can specify a click listener on a view as follows:
android:onClickListener="@{callbacks.clickListener}"

(Supposing you have declared a variable called “callbacks” within data tags and passed an instance of it to your binding object.)

Similarly you can specify an onItemSelectedListener on a spinner:

android:onItemSelected="@{viewModel.spinnerListener}"

2. Another way is to pass a method reference. Suppose you are passing an instance of viewModel as a variable to your binding implementation and you have a method saveItem(view: View) in your view model. You can refer to this method from a button in your xml as follows:

Note that when we use method references, the arguments of the referred method must match the parameter types of the listener. Since onClick callback of the View.OnClickListener expects a View as parameter, your saveItem() method has to have view parameter. If you need to pass something else to the method, you should go for listener bindings instead.

3. So the third option is listener bindings. In this option, we are passing a lambda expression with the parameters we need. We don’t have to stick to the parameters of the event listener. As an example, let’s use a listener binding in the recyclerview data binding implementation we have seen above.

Suppose we have declared an interface as follows, for handling item clicks:

Click for Java version

Inside the data tags of our item layout, we’ll define a click listener as a variable:

Then we will attach this listener to a view (typically root view, for list item clicks), as follows:

See full layout here

Just like we would do without data binding, we again implement the interface in our host activity/fragment and pass a listener instance to recyclerview adapter. (I assume you know these). Then inside our bind() method in custom view holder, we set a listener to each item like:

//Kotlin
binding.productItemClick = mListener
//Java
binding.setProductItemClick(mListener);

Remember that the library creates getters and setters for all variables we declare in data tags according to conventions. Since we named our click listener as productItemClick, its setter is called as setProductItemClick. So your setters will be named according to the name you gave to your variables.

In the next article, we will talk about binding adapters and observable objects.

--

--