Android Data Binding: Animations

When Data Doesn’t Change in the Blink of an Eye

I’ve shown how to use Android Data Binding to update the View when the data changes. It is a great way to see instant updates to the UI, but sometimes you want to draw attention to the data change or just provide a smooth transition. There are two ways to add animations using data binding: use a binding adapter or use OnRebindCallback.

Binding Adapter Animations

A binding adapter is a method that sets the value for a View when a data bound value changes. Using a binding adapter for animations is very straightforward, but if you need a refresher, take a look at the Custom Setters article. We can use a binding adapter to intercept the value change and animate it.

Here is one that fades a View in or out:

@BindingAdapter("animatedVisibility")

public static void setVisibility(final View view,

final int visibility) {

// Were we animating before? If so, what was the visibility?

Integer endAnimVisibility =

(Integer) view.getTag(R.id.finalVisibility);

int oldVisibility = endAnimVisibility == null

? view.getVisibility()

: endAnimVisibility;



if (oldVisibility == visibility) {

// just let it finish any current animation.

return;

}



boolean isVisibile = oldVisibility == View.VISIBLE;

boolean willBeVisible = visibility == View.VISIBLE;



view.setVisibility(View.VISIBLE);

float startAlpha = isVisibile ? 1f : 0f;

if (endAnimVisibility != null) {

startAlpha = view.getAlpha();

}

float endAlpha = willBeVisible ? 1f : 0f;



// Now create an animator

ObjectAnimator alpha = ObjectAnimator.ofFloat(view,

View.ALPHA, startAlpha, endAlpha);

alpha.setAutoCancel(true);



alpha.addListener(new AnimatorListenerAdapter() {

private boolean isCanceled;



@Override

public void onAnimationStart(Animator anim) {

view.setTag(R.id.finalVisibility, visibility);

}



@Override

public void onAnimationCancel(Animator anim) {

isCanceled = true;

}



@Override

public void onAnimationEnd(Animator anim) {

view.setTag(R.id.finalVisibility, null);

if (!isCanceled) {

view.setAlpha(1f);

view.setVisibility(visibility);

}

}

});

alpha.start();

}

You can see that I took into account the current alpha when the View is in the process of animating. It is a little extra work, but it will help the users with a smooth animation experience. To make it even better, you could also take into account the alpha in the duration so that when a fade in is interrupted by a fade out, the fade out will be shorter. Since this is a short animation, it doesn’t look too bad, even when interrupted.

I chose to use a custom attribute so that only the views that I use “app:androidVisiblity” with will fade in and out. If I had created a binding adapter for “android:visibility,” all views that have binding expressions on that attribute would fade in and out.

OnRebindCallback

ViewDataBinding has a listener that lets you have fine control over the binding steps, OnRebindCallback. You can use this to intercept the binding before it actually makes a change. It doesn’t tell you what change is going to be made (it doesn’t know yet), so it doesn’t work like the binding adapter. However, this is perfect for TransitionManager.beginDelayedTransition(), which must be called immediately before changes are made.

Chet Haase did a great DevByte on it (and an article on Transition support for earlier releases), but I’ll summarize what Transitions do. When you call beginDelayedTransition(), the Transition captures the state of the view hierarchy. After the next layout, the Transition captures the end state. Then, animations are generated for the Views that change.

binding.addOnRebindCallback(new OnRebindCallback() {

@Override

public boolean onPreBind(ViewDataBinding binding) {

TransitionManager.beginDelayedTransition(

(ViewGroup)binding.getRoot());

return super.onPreBind(binding);

}

});

TransitionManager creates all the animations for me, so it is very easy. On the other hand, this is called when a binding expression gets dirty and that has additional overhead. Fortunately, this is typically only once per frame, so if you change several parts of your data model, you’ll only see the overhead once.

Which One Do I Use?

Advantages of the BindingAdapter mechanism:

Fine-grained control — only the views you want to animate will animate

Less overhead than transitions (performance)

Very flexible — you can create whatever animation you want

Advantages of the OnRebindCallback mechanism:

Simple to use

Don’t have to use custom attributes (or override default behavior)

Can animate many things with the same code (see Transition subclasses)

You can see that they’re both useful and you can even combine them. If you do that, the Transition should exclude Views that you want to use a binding adapter with.

With these two techniques, you’ll be able to provide smooth animations when data changes that will delight your users.