Set The Scene With The New Transitions Framework

Written by: on November 19

One of the pain points during the dark ages of Android was the animation framework.

It frequently took several lines of code to get a simple animation to run, and even when you did, it often performed poorly. Finally, in Android 3.0, property animations were introduced, which now made animations easier to work with. With Project Butter, these animations could now take full advantage of the power of the GPU. All of these advancements have been great for developers, but creating complex view changes still required a lot of math and state checking.

Enter the new Scene and Transitions framework. Simple transitions are now trivial, and you can actually pull off some fairly complex scene changes with a relatively small amount of code.

Easy Mode

Before changing any of the properties on a ViewGroups’s children, call TransitionManager.beginDelayedTransition(), then change the properties on the children as you normally would. This might include adding text to a text view, changing the visibility of a view, or changing the gravity or position. When Android goes to re-draw these views, it will automatically animate the changes.

Aside from the simplicity, this has the added benefit of being easily integrated into existing apps that support old versions of Android. Add an API check around the beginDelayedTransition() call, and old versions will have items pop in and out like they always have. Once a user gets a new phone/OS upgrade, these transitions will be animated!

Scene To Scene

Scenes are wrappers around normal Android layouts. They allow you to specify a common scene root that the transition framework will use to animate changes.

To create a new scene, create a XML layout as you normally would. In this case, I’m going to create a layout named scene_1.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/scene_base"
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <TextView
        android:id="@+id/welcome_text"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="This is the first scene!"
        android:layout_centerHorizontal="true"/>

    <Button
        android:id="@+id/press_me"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_below="@id/welcome_text"
        android:text="Press me!"
        android:gravity="center_horizontal"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

Then create a secondary scene using another XML layout (scene_2.xml).

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_base"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/welcome_text"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="This is the second scene"
        android:layout_centerHorizontal="true" />

    <TextView
        android:id="@+id/second_line"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="It contains 2 views with the same IDs as scene_1"
        android:layout_below="@id/welcome_text"
        android:layout_centerHorizontal="true" />

    <TextView
        android:id="@+id/third_line"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="But it also contains two new lines of text"
        android:layout_below="@id/second_line"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/press_me"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="I'm done"
        android:gravity="center_horizontal"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

Any common layout IDs will automatically animate from one scene to another. To start a transition, call TransitionManager.go() – this will take a scene object as well as an optional transition. Not passing a transition will result in an automatic transition, which will use a combination of changing bounds and fading that works well for most cases.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Set the layout to the initial scene
        setContentView(R.layout.scene_1);

        //grab the root element from the XML
        RelativeLayout sceneBase = (RelativeLayout)findViewById(R.id.scene_base);

        //Create a new layout for the second scene
        ViewGroup scene2Group = (ViewGroup)getLayoutInflater().inflate(R.layout.scene_2, sceneBase, false);
        //Create a scene using the root element from the initial scene
        //plus the new group we just created
        final Scene scene2 = new Scene(sceneBase, scene2Group);

        //When the user clicks the button transition from scene1 to scene2
        Button button = (Button)sceneBase.findViewById(R.id.press_me);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TransitionManager.go(scene2);
            }
        });
    }

Beware Scene.getSceneForLayout()

There is currently a bug in the transition framework that prevents scenes built using Scene.getSceneForLayout() from animating correctly on the second pass through an activity. Dave Smith has written up a detailed bug here describing the problem. As it stands now, using this static method will result in an app that is broken, so stay away!

Wrap-Up

The new transition framework is incredibly powerful, but it is only available for API 19. If you’re not doing any animations currently, adding beginDelayedTransition() is a great start since it only involves a single line. More advanced transitions, while simple to pull off for Kit Kat devices, will still require fall-back code for older devices.

Last of all, keep your eye on the getSceneForLayout() bug. This function is very powerful when it works, as it lazily inflates new scenes as you transition from scene to scene. This can be manually done, of course, but will take quite a bit micro-management.

You can find a small sample project of the examples here to get you started.

This piece is the seventh of eight in our KitKat Developer’s Guide. Check back later this week for new updates or follow us on twitter.

Casey Vincent

Casey Vincent is a senior engineer at Double Encore, specializing in Android Development. Casey also has a passion/weakness for Windows Phone devices and cake.

Article


Add your voice to the discussion: