Android Data Binding Tips
Tip 1: Create a Visibility Binding Adapter
I've been working with Android Data Binding for a few months now. In that time, I have discovered a few patterns and practices that have made development with Data Binding successful and ...dare I say... fun!
On occasion, I will share them here on the blog. In this first installment, we'll create a binding adapter to control a view elements visibility.
Android Data Binding expressions are powerful. Nevertheless, sometimes the expressions can get pretty ugly. One common expression you may find yourself doing is showing and hiding a view based on a variable defined in the data section of the layout. If you have used data binding before, you may have written something like this:
<layout>
<data>
<import type="android.view.View"/>
<variable name="show" type="Boolean"/>
</data>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{show ? View.VISIBLE : View.GONE}"/>
</layout>I, for one, really hate typing this out. First, I need to remember to import the android.view.View type. On top of that, I need to ensure that I get the constants capitalized - Android Studio does not support autocomplete in binding expressions. Ugh!
A More Elegant Syntax
When declaring visibility, I either toggle between VISIBLE and GONE or VISIBLE and INVISIBLE, but never toggle among all three. Therefore, I would prefer to declare the state as a boolean and have the element react accordingly. Because the hidden state is either GONE or INVISIBLE, I can use that state as the attribute name. In the following snippet, I declare the attribute as app:isGone with a data-bound value of !show. I pass the ! to invert the boolean’s value - in this attribute, a true value will hide the view.
<layout>
<data>
<variable name="show" type="Boolean"/>
</data>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:isGone="@{!show}" />
</layout>Binding Adapters to the Rescue
With the preferred syntax in place, I can create a binding adapter. The adapter can be added to any class; however, I would add this type of adapter to a ViewBindingAdapters class in a core namespace to follow the Data Binding library’s convention.
As a review, to create a binding adapter, you create a static void method with a @BindingAdapter annotation. In its simplest form, the annotation contains a value to match the attribute we want to bind to. Because we are using an attribute that will not be namespaced as android:*, we omit the namespace. The method parameters are the view type we want to adapt and the value expected from the attribute.
// ViewBindingAdapters.java
@BindingAdapter("isGone")
public static void setIsGone(View view, boolean hide){
view.setVisibility(hide ? View.GONE : View.VISIBLE);
}
@BindingAdapter("isInvisible")
public static void setIsInvisible(View view, boolean hide){
view.setVisibility(hide ? View.INVISIBLE : View.VISIBLE);
}Now, I no longer need to type that ternary expression in my layouts. When using the attribute, remember that a true expression will signal the adapter to hide your view (instead of show it). Hopefully, this will be useful for you too! Let me know what you think!