Things that you’ll know about DataBinding in future

Bogdan Voiko
AndroidPub
Published in
3 min readSep 7, 2017

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

There are barely few StackOverflow posts that can tell you that and zero official docs, but I bet that it’s thing you want to know.

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

When I came around binding one view to another view properties and received “could not resolve the two-way databinding attribute ….”, I was like “Okay, screw it, I’ll find another way”. But now it’s another thing.

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

Sometimes you want to set some attribute of view that you know that’s exists, but DataBinding library keeps telling you that it can’t find setter for it. Of course it can’t, because it relies only on two things: defined @BindingAdapters and named setters. And once you understand how it searches for setters, you’ll abuse it forever.

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 :)

--

--