Transitions in the Android Support Library

Now available back to API 14

Android KitKat offered a new feature called “Scenes & Transitions,” with the intention of making animations easier and more automatic for developers. Instead of thinking about how to create animations to help guide the user between different phases of an application flow, a developer could simply use transitions and have animations run automatically whenever the UI changed. These transitions could also be customized to offer new or different animation effects for these UI changes.

The problem was that these APIs, being a part of the Android platform itself, were only available on KitKat and later releases. Developers wanting animated experiences for all of their users weren’t terribly interested in transitions because they’d still have to do things the old way for the older releases they needed to support.

The intention with transitions was always to provide a back-ported solution for the Support Library… but it was a non-trivial task and thus didn’t happen. Until now.

Thanks to the efforts of Yuichi Araki in the Google Developer Relations team, we’ve finally finished that back-port and the capabilities of KitKat transitions are available all the way back to the Ice Cream Sandwich release. That means that you are now able to change your UI’s layout and use the transitions system to animate those changes automatically on devices back to API level 14.

Using the API

There are many aspects to the transitions API that I won’t get into in this short-attention-span article (but I encourage you to read this overview). For example, it is possible to set up “scenes” (think of them as configuring a layout) and then animate between them using either default or custom animations.

But for the purposes of this article, I’ll just show how to use my favorite method (because it’s the simplest), beginDelayedTransition(). This static method on TransitionManager takes a ViewGroup parameter, which defines the “scene root” or the container in which the transition will run. When you call the method, two things happen: all of the views in the scene root’s hierarchy are examined and their relevant property values are cached. Second, an OnPreDrawListener is added to that scene root which triggers after the next layout.

Now you are free to make layout changes to the views in that hierarchy and a layout will be triggered. When that layout runs, the OnPreDrawListener is called and the transition system retrieves the new property values of the views in the hierarchy. Now that the values before and after the layout change are known, the system calculates the deltas and creates and runs the appropriate animations. And that’s it! The best part about this approach is that you only needed to call one single method: beginDelayedTransition(). The transition system did the rest of the work on your behalf.

Transitions demo running on Jellybean

SimpleTransition

We can see how this works in a simple example app hosted on the android-ui-toolkit-demos project on GitHub, cleverly named SimpleTransition. In this app, there is a group of four buttons aligned above one another. I happened to use RelativeLayout for this simple demo, but the actual layout used is irrelevant, because the transitions system doesn’t care.

Clicking on one of the buttons will reposition them in the corner specified in the button label (Top Left, Bottom Right, etc.). This happens because each button calls a click handler with this code in it:

TransitionManager.beginDelayedTransition(mSceneRoot);

After that, the click handler rearranges the buttons by changing the layout parameters accordingly:

params = (LayoutParams) mFirstButton.getLayoutParams();
// remove old rules, add new ones
params.addRule(...);
// ...
mFirstButton.setLayoutParams(params);

… and that’s all. The calls to setLayoutParams() on the buttons will call View.requestLayout() internally, which results in a layout pass on the next frame. That layout pass will then trigger a call to the transition system’s OnPreDrawListener where the final positions of the buttons will be recorded and the transition animations will be created and started.

By default, the transition system runs AutoTransition on all of the affected views. AutoTransition runs, in sequence, a Fade transition on views that are removed, a ChangeBounds transition (which moves and resizes views) on views that change position/size, and a Fade transition on views that are added. In the case of this demo, the four buttons simply change position, so ChangeBounds is the only transition that runs, in order to animate those position changes.

There is another overload of beginDelayedTransition() which takes an additional parameter that is a Transition instance. Using this method, we can customize the types of animations that are run.

As a [very] simple way of showing how to create a custom transition, the demo creates a TransitionSet (which holds a collection of child transitions and runs them in sequence or in parallel):

mStaggeredTransition = new TransitionSet();
Transition first = new ChangeBounds();
Transition second = new ChangeBounds();
Transition third = new ChangeBounds();
Transition fourth = new ChangeBounds();

first.addTarget(mFirstButton);
second.setStartDelay(50).addTarget(mSecondButton);
third.setStartDelay(100).addTarget(mThirdButton);
fourth.setStartDelay(150).addTarget(mFourthButton);

mStaggeredTransition.addTransition(first).addTransition(second).addTransition(third).
addTransition(fourth);

Unlike the default transition (AutoTransition), this custom transition does not bother with the Fade transition because we know, in this app, that we will only ever be changing the view positions. We create each of the child transitions to specifically target one of the buttons using addTarget() and set a staggered startDelay on each of them, which will cause each of the buttons to start animating slightly after the button before it. Then we add these transitions to mStaggeredTransition, which will play them in parallel, thus starting each of the child animations slightly offset in time by the startDelay. We could, alternatively, tell the transition set to sequence the transitions after one another (which is how AutoTransition is constructed with its Fade/ChangeBounds/Fade transitions), but that would mean each of the buttons would animate all the way to the end before the next transition started, which is not the effect I was going for.

When the user checks the “Stagger” checkbox in the UI, that causes the click handlers to issue this call to beginDelayedTransition(), which uses our custom transition:

TransitionManager.beginDelayedTransition(mSceneRoot,
mStaggeredTransition);

Go! Play! Transition Things! Get Moving!

Transitions can be a powerful tool. More importantly, they can be a very simple way to enable rich UI experiences for users without developers having to learn a great deal about animation. You focus on the application logic; transitions can worry about how to make the animations work.

For More Information

Check out some of the overviews, docs, videos, and sample code to learn more about how transitions work and how to use them in your applications:

Support Library SimpleTransition Demo App

Animating Views Using Scenes and Transitions

DevByte: Android 4.4 Transitions

Android Support Library

Support Library Transitions Reference Documentation