January 14th, 2013

Android Tricks: Eye Candy Using Layout Animations

Since Android 3.0 (API level 11), a lot of effort has gone into improving the visual appeal of the operating system. In addition to adding eye candy to the builtin software, Google also introduced new features in the Android development frameworks that make it easier for third-party developers to improve the look and feel of their own applications. One such improvement is the ability to add animations to existing ViewGroup elements (i.e. every layout object) without having to write the code by hand.

When using the android:animateLayoutChanges attribute with a ViewGroup, a set of default animations are registered on the ViewGroup‘s child views during layout changes. By default, visibility and size modifications are taken into account. The first produces a fading effect and the second produces a sliding animation.

These two default effects can be used together with a LinearLayout to make a nice transition between two states of the same widget:

In the video above, my widget has two different presentation modes: one for user selection and the other for drag-and-drop interaction. The latter mode is triggered by a user action (starting the drag).

Here is the relevant part of my layout XML for the widget skeleton:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_marginTop="16dp"
    android:layout_marginRight="16dp"
    android:animateLayoutChanges="true"
    android:id="@+id/animatedLayout">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="7dp"
        android:id="@+id/mode1">

		<!-- Widget mode 1 layout -->

    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="1px"
        android:layout_gravity="center"
        android:id="@+id/mode2">

        <!-- Widget mode 2 layout -->

    </LinearLayout>
</LinearLayout>

The outer LinearLayout is the one that contains the android:animateLayoutChanges attribute. Both widget modes are also LinearLayout objects.

Since the first mode is the one shown by default, its layout height is set to wrap_content whereas the second mode layout is artificially hidden by setting its height to 1 pixel.

To go from mode 1 to mode 2, I use the following code in the drag started event handler:

var parms = mode2.LayoutParameters as LinearLayout.LayoutParams;
parms = new LinearLayout.LayoutParams (parms) {
	Height = ViewGroup.LayoutParams.WrapContent,
	Gravity = GravityFlags.Center
};
mode2.LayoutParameters = parms;
mode1.Visibility = ViewStates.Gone;

And to go the other way, we simply restore the XML state we had beforehand with a similar code:

mode1.Visibility = ViewStates.Visible;
var parms = mode2.LayoutParameters as LinearLayout.LayoutParams;
parms = new LinearLayout.LayoutParams (parms) {
	Height = 1,
	Gravity = GravityFlags.Center
};
mode2.LayoutParameters = parms;