Android Data Binding: The Big Event

And You Don’t Even Have to Dress Up

In previous articles, I wrote about how to eliminate findViewById from Android applications and in some cases eliminate the need for View IDs altogether. One thing I didn’t explicitly mention in those articles is how to handle event listeners, such as View’s OnClickListener and TextView’s TextWatcher.

Android Data Binding provides three mechanisms to set an event listener in the layout file and you can choose whichever is most convenient for you. Unlike the standard Android onClick attribute, none of the event data binding mechanisms use reflection, so performance is good whichever mechanism you choose.

Listener Objects

For any view with a listener that uses a set* call (as opposed to an add* call), you can bind a listener object to the attribute. For example:

<View android:onClickListener="@{callbacks.clickListener}" .../>

Where the listener is defined with a getter or a public field like:

public class Callbacks {
public View.OnClickListener clickListener;
}

There is also a shortcut for this where the “Listener” has been stripped:

<View android:onClick="@{listeners.clickListener}" .../>

Binding with listener objects is used when your application already uses them, but in most cases you’ll use one of the other two methods.

Method References

With method references, you can hook a method up to any event listener method individually. Any static or instance method may be used as long as it has the same parameters and return type as in the listener. For example:

<EditText
android:afterTextChanged="@{callbacks::nameChanged}" ...
/>

where Callbacks has a nameChanged method declared like this:

public class Callbacks {
public void nameChanged(Editable editable) {
//...
}
}

The attribute used is in the “android” namespace and matches the name of the method in the listener.

Though it isn’t recommended, you may do some logic in the binding as well:

<EditText android:afterTextChanged=
"@{user.hasName?callbacks::nameChanged:callbacks::idChanged}"
...
/>

In most cases it is better to put logic in the called method. This becomes much easier when you can pass additional information to the method (like user above). You can do this with lambda expressions.

Lambda Expressions

You can supply a lambda expression and pass any parameters to your method that you wish. For example:

<EditText
android:afterTextChanged="@{(e)->callbacks.textChanged(user, e)}"
...
/>

And the textChanged method takes the passed parameters:

public class Callbacks {
public void textChanged(User user, Editable editable) {
if (user.hasName()) {
//...
} else {
//...
}
}
}

If you don’t need any of the parameters from the listener, you can remove them with this syntax:

<EditText
android:afterTextChanged="@{()->callbacks.textChanged(user)}"
...
/>

But you can’t take just some of them — it is all or none.

The timing of expression evaluation also differs between method references and lambda expressions. With method references, the expression is evaluated at binding time. With lambda expressions, it is evaluated when the event occurs.

Suppose, for example, the callbacks variable hasn’t been set. With a method reference, the expression evaluates to null and no listener will be set. With lambda expressions, a listener is always set and the expression is evaluated when the event is raised. Normally this doesn’t matter much, but when there is a return value, the default Java value will be returned instead of having no call. For example:

<View android:onLongClick=”@{()->callbacks.longClick()}” …/>

When callbacks is null, false is returned. You can use a longer expression to return the type you wish to return in such an error case:

<View android:onLongClick=”@{()->callbacks == null ? true : callbacks.longClick()}” …/>

You’ll more often just avoid that situation altogether by ensuring that you don’t have null expression evaluation.

Lambda expressions may be used on the same attributes as method references, so you can easily switch between them.

Which To Use?

The most flexible mechanism is a lambda expression, which allows you to give different parameters to your callback than the event listener provides.

In many cases, your callback will take the exact same parameters as given in the listener method. In that case, method references provide a shorter syntax and are slightly easier to read.

In applications that you are converting to use Android Data Binding, you may already have listener objects that you were setting on views. You can pass the listener as a variable to the layout and assign it to the view.