Things that you’ll know about DataBinding in future

I’m using Android DataBinding for a long time now and I completely fell in love with it. As you know the more time you spend with your loved one the more secrets you know and some of them you would like to know earlier.

First thing: You can use context in DataBinding layout files

So, imagine that you have class with function like this :

class MyButtonHandler {

public static int getColorForButton(Context context, int buttonType) {
switch (buttonType) {
case 0:
return ContextCompat.getColor(context, android.R.color.holo_green_light);
case 1:
return ContextCompat.getColor(context, android.R.color.holo_red_light);
....
}
}
}

And you can use it as simply as this:

<data>
<import type="com.mypackage.MyButtonHandler" />

<variable
name="type"
type="int/>
</data>
...<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@{MyButtonHandler.getColorForButton(context,type)}" />

As you can see, only variable that we declared is type , and context will be provided by ViewDataBinding .

Second thing: don’t be afraid to create custom two-way binding

Lets imagine that you want to bind your view padding to another’s view height (really I don’t think that you’ll want this particular example, but idea is common for all). You can do it like so:

<View
android:layout_width="16dp"
android:layout_height="16dp"
android:paddingLeft="@{otherView.height}"/>

And you’ll receive error like this:

Could not resolve the two-way binding attribute ‘height’ on type ‘android.widget.FrameLayout’    (It can be any type of View)

That’s expected, because system doesn’t know how to do and track such crazy things, but we know how.

First of all we need to tell the system about our height attribute.

@InverseBindingAdapter(attribute = "android:height")
public static int getHeightFromView(View view) {
return view.getHeight();
}

And then we need to tell the system how to watch changes of this attribute

@BindingAdapter("android:heightAttrChanged")
public static void setHeightWatcher(View view, final InverseBindingListener listener) {
View.OnLayoutChangeListener newValue = new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
boolean isChanged = left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom;
if (isChanged)
listener.onChange();
}
};
View.OnLayoutChangeListener oldValue = ListenerUtil.trackListener(view, newValue, R.id.layoutHeightChange);
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue);
}
view.addOnLayoutChangeListener(newValue);
}

Note there 2 things:

  1. android:heightAttrChanged this name is not random, it corresponds to the rule attr_name + “AttrChanged" ;
  2. ListenerUtil.trackListener will save you from context leaks, so be sure to setup it properly.

And there you have it, your view will react to height change of other view. Of course you might use it for more obvious stuff (like it’s already done for TextView’s text change, CheckBox’s check state change and so on). Be creative here :)

Third thing: set what ever you want to

Let’s say you want to bind transientState.

app:transientState="@{true}"

I bet you receive error on it, because actual setter for it called setHasTransientState . Note that difference is in “has” word.

But when you try to use full word, all will be OK.

app:hasTransientState="@{true}"

You see now? DataBinding will simply look for set + “property_name” (that’s actually Java Beans naming) and as long as you will stick to this naming rule — all your sets will be executed correctly.

And that’s all, that was 3 things that I wish I knew earlier. Thanks for attention and happy coding :)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store