5-Feb-09 (Created: 5-Feb-09) | More in 'Android Animations'

layout animation

As you have seen, Frame by Frame animation is a quick and dirty way to add visual effects to your Android applications. The second type of animation, the "Layout Animation" is almost as simple. In Android Lists and View Grids are the two most common controls that are used. Using Layout Animation Android provides a quick and dirty way to add visual affects to the way each item in a list view is displayed using animation. Same is true with views in a grid control. In fact the principle applies to all controls derived from a ViewGroup.

In this seciton we will cover a simple test harness to learn, test, and experiment with the Layout animation capabilities. We will show you how you can attach an animation like scaling, rotation, translation, or alpha to a ListView. We will also introduce and explain the idea of interpolators and their role in animation. The SDK documentation on interpolators is a bit vague. We will show you the relevent source code snippets to make interpolator behavior very clear. We will also cover something called a "LayoutAnimationController" that mediates between an animation and a view group. These layout controllers are specialized for the type of ViewGroup they need to adapt the animation for.

Planning the Layout Animation Test Harness

All of the Layout Animation concepts covered in this section can be tested using a simple list view set in an Activity. Once we have a list view we can attach an animation to that list view so that each list item will go through that animation. Assume we have a scale animation that makes a view grow from 0 to its original size on the "Y" axis. We can attach that animation to a List View. When this happens the ListView will animate each item in that list view using this animation. There are some additional parameters that apply outside of a basic animation, when this animation is applied to a list view. You may want to animate the list from top to bottom or bottom to top etc. These parameters are specified through an intermediate class that acts as a mediator between the individual animation and the list. Both the individual animation and the mediator can both be defined in XML files in the "/res/anim" sub directory. Once we have the mediator xml file, we can use that file as an input to the ListView in the layout xml definition. Once we have this basic setup working we can start altering the individual animations to see how they impact the display of the ListView. The examples provided here cover scaling, tranlsation, rotation, alpha, and a combination of translation and alpha. We will go through all these steps as we develop the examples here. By the end of this section you will know what we are talking about here.

As we embark on this exercise let us show you how the ListView looks like when finished.

Figure 5-4 Animated List View

Creating the Activity and the List View

Let us start by creating a lay out xml so that we can load that lay out in a basic Activity. Here is the code for a simple layout with a list view in it. You will need to place this file in the "/res/layout" sub directory. Assuming the filename is "list_layout.xml", your complete file will be at "/res/layout/list_layout.xml"


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

    <ListView
        android:id="@+id/list_view_id"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />
</LinearLayout>

This is a very simple linear layout with a single list view in it. Let us go ahead and see the code for the Activity class that loads this view. However there is one point worth mentioning in the definition of the ListView. If you happen to work through the notepad examples and other Android examples more than often you will see that the "id" for the ListView is usually specified as "@android:id/list". As we have talked about in Chapter 3 the resource reference "@android:id/list" points to an "id" that is predefined in the "android" namespace. The question is when do we use this "id" vs our own id such as "@+id/list_view_id"? You will only need to use "@android:id/list" only if the Activity is a "ListActivity". A ListActivity assumes that there is a ListView identified by this pre-determined id that it can load. In our case we are not using a ListActivity but a general purpose Activity and we are going to be explicitly populating the ListView. As a result there are no restrictions on the kind of id we can allocate. However we do have the option of also using "@android:id/list" because it doesn't conflict with anything as there is no ListActivity in sight. This surely is a digression but worth noting it as you create your own ListViews outside of a ListActivity.

Now that we have the layout needed for our Activity let us go ahead and show the code for the Activity.


public class LayoutAnimationActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_layout);
        setupListView();
    }
    private void setupListView()
    {
    	  String[] listItems = new String[] {
    	        "Item 1", "Item 2", "Item 3",
    	        "Item 4", "Item 5", "Item 6",
    	  };
    	  
    	  ArrayAdapter listItemAdapter = 
    		   new ArrayAdapter(this
    		           ,android.R.layout.simple_list_item_1
    		           ,listItems);
    	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
    	  lv.setAdapter(listItemAdapter);
    }
}

Some of this code is simple and some is not. The first part of the code just loads the view based on the generated layout id "R.layout.list_layout". Out goal is to take the list view from this layout and populate it with 6 text items. These text items are loaded up into an array. We will need a data adapter to be set into a ListView so that the ListView can show those items. To create the necessary adapter, you will need to specify the how each item will be seen or laid out when the list is displayed. The layout is specified by using a predefined layout in the base android framework. In this example this layout is specified as


android.R.layout.simple_list_item_1

The other possible view layouts for these items include


simple_list_item_2
simple_list_item_checked
simple_list_item_multiple_choice
simple_list_item_single_choice

You can see the Android documentation to see how each of these layouts look like and behave. You can also change here in the code and see how the UI look like.

This code is sufficient to show the ListView the way we would like to see for this example. You can invoke this activity from any menu item in your application using the following code


Intent intent = new Intent(inActivity,LayoutAnimationActivity.class);
inActivity.startActivity(intent);

You will need to register the LayoutAnimationActivity in the android manifest file for the above intent invocation to work. Here is the code for it.

satya include android manifest file

Animating the ListView

Let us see how we can apply Scale animation to this ListView. Let us take a look at how this scale animation is defined in an XML file.


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
   <scale 
         android:fromXScale="1"
         android:toXScale="1"
         android:fromYScale="0.1"
         android:toYScale="1.0"
         android:duration="500"
         android:pivotX="50%"
         android:pivotY="50%"
         android:startOffset="100" />
</set>

These files reside under "/res/anim" subdirectory. Let us put in English what these pararemeters are saying. The "from" and "to" scales point out the starting and ending magnification factor. In the example here the magnification starts at 1 and stays at 1 on the x-axis. This means the list items will not grow or shrink on the x axis. Where as on the "y" axis they start out small, to be precise 1/10th of their normal size, and then grow to reach their normal size. It will take "500" milliseconds to complete the scaling operation. The center of action is half way between x and y. The startoffset refers to the number of milliseconds to wait before starting the animation.

The parent node of "scale" animation points to an animation "set" that could allow more than one transformation to be in effect. We will cover one of those examples as well. But for now there is only one animation in this set.

Let us place this file as "/res/anim/scale.xml". We are not ready yet to set this animation XML as an argument to the ListView. ListView requires another XML file that acts as a mediator between this animation set and the ListView. Here is the XML file that describes that mediation


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/scale" />

You will also need to place this file in the "/res/anim" sub directory. For our example assume that the filename is "list_layout_controller". Once you look at this definition we could see why this intermediate file is necessary. This XML is saying that start the animation in the reverse order for the items in the list. It is also saying that start animating each item with a 30% delay to the total duration of the animation of a given item. This XML also refers to the individual animation file, in this case the "scale.xml". Notice that instead of the filename the code uses the resource reference.

It is worth noting at this point that every XML representation in Android has an equivalent Java class. The "scale" tag is equivalent to "android.animation.ScaleAnimation". The "layoutAnimation" tag corresponds to "android.animation.LayoutAnimationController". You can refer to the Android SDk documentation on animation on how to construct the same objects programmatically and attach them to the ListView. In this example however we will stick to the XML route only.

Now that we have the necessary input XML files, let us show you how to update the ListView XML definition to include this animation XML as an argument. Before we do that let us review the XML files we got so far


/res/anim/scale.xml.xml               // indiviual scale animation
/res/anim/list_layout_controller.xml  // the animation mediator file
/res/layout/list_layout.xml           // the activity view lay out file

With these files in place, we need to modify the "list_layout.xmL", the layout xml, file to have the ListView point to the "list_layout_controller.xm". Here is the updated code for the "list_layout.xml"


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

    <ListView
        android:id="@+id/list_view_id"
        android:persistentDrawingCache="animation|scrolling"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layoutAnimation="@anim/list_layout_controller" />
        />
</LinearLayout>

The changed lines are high lighted. The key tag is the "android:layoutAnimation". This tag points to the mediating xml file which defined the layout controller using the xml tag "layoutAnimation". The "layoutAnimation" in turn points to the individual animation. In the example we have pursued so far this individual animation is the "Scale" animation defined in "scale.xml". Android also recommends to set th "persistenDrawingCache" to optimize for animation and scrolling. Refer to the Android SDK documentation for more details on this tag.

When you update this ListView XML the ADT plugin in Eclipse will automatically recompile the package taking this change into account. If you were to run the application now you will see the "Scale" animation take affect on the individual items. We have set the duration to 500 milliseconds so that you can observe the scale change clearly as each item is drawn.

We are now in a position to experiment with different animation types. Let us try now "alpha" animation. To do this create a file called "/res/anim/alpha.xml" with the following content in it


<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:interpolator="@android:anim/accelerate_interpolator"
       android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />

Alpha animation is responsible for controlling the fading of color. In this example here we are asking the alpha animation to start from invisible shade to the actual shade in 1 second. Make sure the duration is 1 second or larger. Otherwise the color change is harder to notice.

Everytime you want to change this individual item animation like this, you will need to change the layout animation XML file to point to this new animation file. Here is how change the animation from "scale" animation to "alpha" animation


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/alpha" />

The changed line in the layout animation xml file is highlighted.

Let us now try an animation that is a combination of movement and color gradient. Here is the sample XML for this


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromYDelta="-100%" android:toYDelta="0" 
android:duration="500" />
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
android:duration="500" />
</set>

Notice how we have specified two animations in animation set. The "translate" animation will move the text from top to bottom in its current allocated display space. The "alpha" animation will change the color gradient from invisible to visible as the text item is descending into its slot. The duration of "500" is important to perceive the change in a comfortable fashion.

ofcourse, again, you will have to change the layout animation xml file with a reference to this filename. Assuming the filename for this animation is "/res/anim/translate-alpha.xml" your layout animation xml file will look like


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/translate-alpha" />

Let us try now the "rotation" animation. Here is the sample code


<rotate xmlns:android="http://schemas.android.com/apk/res/android"
       android:interpolator="@android:anim/accelerate_interpolator"
       android:fromDegrees="0.0" 
      android:toDegrees="360"
      android:pivotX="50%"
      android:pivotY="50%"
      android:duration="500" />

This code will spin the text item one full circle around the mid point of the text item. The duration of 500 milliseconds is a good amount of duration to perceive the rotation.

This concludes the basic concepts in layout animation where we start with a simple animation file and associate it to a ListView through an intermediate layout animation xml file. That is all that is needed to see the animated effects. However there is one item that got left un explained. This item is the idea of an "interpolator". The next subsection will talk about interpolators.

Interpolators

Interpolators tell an animation how a certain property such as a color gradient change over time. Will it change in a linear fashion, or in an exponential fashion, or will it start quick and slow down toward the end, etc. Consider the following alpha animation that was introduced earlier


<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:interpolator="@android:anim/accelerate_interpolator"
       android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />

The animation identifies the "interpolator" it wants to use. In this case it is the "acclerate_interpolator". There is a corresponding java object that defines this interpolator. Also if you notice this interpolator is spcified as a resource reference. This means there must be a file corresponding to the "anim/acclerate_interpolator" that describes what this java object look like and what additional parameters it may take. That indeed is the case. Let us take a look at that XML file definition for "@android:anim/accelerate_interpolator"


<accelerateInterpolator 
  xmlns:android="http://schemas.android.com/apk/res/android" 
  factor="1" />

This file can be seen in the android package at


/res/anim/accelerate_interpolator.xml

This xml tag corresponds to a java object called


"android.view.animation.AccelerateInterpolator"

you can look up the javadoc for this class to see what XML tags are available. The goal of this interpolator is to give a multiplication factor given a time interval based on a hyperbolic curve. This will become apparent if you were to look at the source code for this interpolator.


public float getInterpolation(float input) 
{         
   if (mFactor == 1.0f) 
   {  
      return (float)(input * input);  
   } 
   else 
   {  
      return (float)Math.pow(input, 2 * mFactor);  
   }  
} 

Every interpolator implements this method diffrently. In this case if the interpolator is set up so that the factor is 1, then it will return the "square" of the factor othewise it will return a "power" of the input that is further scaled by the factor. So if the factor is "1.5" then we will see a "cubic" function instead of a "square" function.

The supported interpolators include


AccelerateDecelerateInterpolator
AccelerateInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator

You can find the beahior of each of these interpolators at the following url


http://code.google.com/android/reference/android/view/animation/package-summary.html

The javasdoc for each of these classes also point out the XML tags available to control them.

This concludes our section on Layout Animation. We will now move to the third section of this chapter to discuss animating a View programmatically.