6-Dec-08 (Created: 6-Dec-08) | More in 'Android Animations'

10 Understanding Animation

Introduction

Android supports two kinds of animation. One is called "Tweened Animation". The second one is called "Frame by Frame" animation. "Tween" is a short for "in-between" borrowed from traditional animators. Inbetweens are drawings that simulate motion between key frames. There may be 24 inbetweens between two key frames or there could be 12 between two key frames. The former case is called "on ones" and the later ones are called "on twos".

In Android here is how "tweening" works. You start with a view. This view can be any view including a view group with any set of complex composed graphical objects. When tweening animation is attached to this view, the animation gets a call back at regular intervals to change any aspect of the tranformation matrix that would be used to render the view.

Moving an object or scaling an object, or changing the shade of an object can all be represented as a set of matrix transformations. By changing this matrix in the call back of the animation you will render the object at a different place with a different scale or a diferent color.

So at a general level, tweening animation is acomplished by changing the properties of a view at various time intervals during the alloted time of an animation.

Android supports the following types of "tween" animations

AlphaAnimation: Changing Transparency of an object

RotateAnimation: Rotating an object

ScaleAnimation: Scale an object

TranslateAnimation: Move an object

In Android frame by frame animation a series of drawable resources are loaded one after the other. The class AnimationDrawable is responsible for frame by frame animation. Each frame in this animation is a drawable resource.

As you get deeper into Android animation you will see that the distinction is not so clearcut where you can mix both concepts by essentially redrawing the canvas one way or the other.

Important Classes in the Android Animation Repertoire

As we explore through the animation concepts of Android you will touch upon the following classes.

Animation: Animation is the fundamental class that allows you to animate any view. It does this by providing an overriden method call back at regular intervals that allows you to change the graphical transformation matrix. Almost every other concept in android depends on this basic idea. Every derived animation supports the following tags: duration, fillAfter, fillBefore, interpolator, repeatCount, repeatMode, startOffset, and zAdjustment.

Matrix: Matrix is a class that belong to the graphics package and is responsible for 2D transformations including depth and perspective. It may be worthwhile to pick up a basic graphics book to read up on the basic idea of image transformations using linear algebra. It sounds complicated but the idea is quite simple and you can quickly get to terms with it in a couple of hours of reading inroductory chapters on it.

Interpolator: An Animation then depends on an implementation interpolator class that is responsible for interpreting the relative magnitude of values at each sampling point of the animation. The interpolator implementations include AccelerateDecelerateInterpolator, AccelerateInterpolator, CycleInterpolator, DecelerateInterpolator, and LinearInterpolator.

AccelerateInterpolator: Uses a hyperbolic curve to interpolate amplitude values along the animation curve.

DecelerateInterpolator: The complement of the AccelerateInterpolator.

AccelerateDecelerateInterpolator: The rate of change starts and ends slowly but accelerates through the middle.

CycleInterpolator: Uses a sinusoidal pattern for a given number of cycles.

LinearInterpolator: Uses a standard linear gradient where the rate of change is constant

AlphaAnimation: Makes use of the same animation framework but instead of using matrices, uses the color gradient termed "alpha" on the Transformation object to vary. The xml tag for this is "alpha". The children tags include: fromAlpha and toAlpha. The Alpha values will range from 0 to 1.

RotateAnimation: A specialized Animation that uses rotation on the matrix supplied by the Transformation object in the animation callback. This is responsible for rotatin in 2 dimensions. This can be specified in XML. The xml tag for this is "rotate". The children tags are: a) fromDegrees b) toDegrees c) pivotX d) pivotY

ScaleAnimation: A specialized Animation that uses scale operations on the matrix supplied by the Transformation object in the animation callback. This is responsible for animating the changes to size of an object. The xml tag for scaleAnimation is "scale". The children xml tags include: fromXScale, toXScale, fromYScale, toYScale, pivotX, pivotY.

TranslateAnimation: A specialized Animation that uses movement operations on the matrix supplied by the Transformation object in the animation callback. This is responsible for moving an object from one place to another place in an animated fashion. The xml tag for translation animation is "translate". The children xml tags include: fromXDelta, toXDelta, fromYDelta, toYDelta.

AnimationSet: Represents a group of Animations that should be played together. The transformation of each individual animation are composed together into a single transform. If AnimationSet sets any properties that its children also set (for example, duration or fillBefore), the values of AnimationSet override the child values. The xml attribute for an animation set is "set". The children includes common tags that belong any animation. An additional binary child "shareInterpolator" is used to indicate if the children should use the interpolator that is specified at the "set" level.

Camera: Camera is used to provide depth, perspective, or projection to a view. This is familiar concept in computer graphics. A Camera allows you to construct matrices to suit the needs of a projection of a 2D image when viewed from a different angle or at a different depth. Camera is used in association with Tranformation matrix to simulate 3D animations in the 2D space.

LayoutAnimationController: This animation controller is a wrapper for an animation to apply that animation to the individual objects of a layout, especially to a container of views like a ListView. A LayoutAnimation controller can be applied to any ViewGroup. Each view of that view group undergoes that animation in a certain order. A layout animation controller is used to animate a layout's, or a view group's, children. Each child uses the same animation but for every one of them, the animation starts at a different time.

A layout animation controller is used by ViewGroup to compute the delay by which each child's animation start must be offset. The delay is computed by using characteristics of each child, like its index in the view group. This standard implementation computes the delay by multiplying a fixed amount of miliseconds by the index of the child in its parent view group.

Subclasses are supposed to override getDelayForView(android.view.View) to implement a different way of computing the delay. For instance, a GridLayoutAnimationController will compute the delay based on the column and row indices of the child in its parent view group.

The xml tag for a layoutanimationcontroller is "layoutanimation". The children tags include a reference to the animation tag (animation), the order in which animations are started (animationOrder:normal, random, or reverse), the amount of delay between each view animation expressed as a fraction (delay), a reference to the interpolator that interpolates the delay between each animation (interpolator)

GridLayoutAnimationController: This layout animation controller is used to animate a grid layout's children. While LayoutAnimationController relies only on the index of the child in the view group to compute the animation delay, this class uses both the X and Y coordinates of the child within a grid. In addition, the animation direction can be controlled. The default direction is DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM. You can also set the animation priority to columns or rows. The default priority is none.

The xml tag for GridLayoutAnimationController is "GridLayoutAnimation". In addition to borrowing the appropriate tags from its parent LayoutAnimationController it has additiona children tags that are: columnDelay, direction, directionPriority, rowDelay. "columnDelay" is a fraction of the animation duration used to delay the beginning of the animation of each column. "direction" is the direction of animation in the grid such as top to bottom or bottom to top, left to right, or right to left etc. "directionPriority" will indicate animation by column or by row or both.

View: A view is the basic unit of animation. You can attach an Animation object to a view using setAnimation(Animation) or startAnimation(Animation). The animation can alter the scale, rotation, translation and alpha of a view over time. If the animation is attached to a view that has children, the animation will affect the entire subtree rooted by that node. When an animation is started, the framework will take care of redrawing the appropriate views until the animation completes. The animation related methods on a view include clearAnimation, startAnimation, setAnimation, getAnimation, onAnimationEnd, onAnimationStart.

ViewGroup: ViewGroup has a number of animation methods that pertain to animating a group of views. Some of the animated related methods on a ViewGroup are: clearDisappearingChildren, getLayoutAnimation, setLayoutAnimation, getLayoutAnimationListener, setLayoutAnimationListener, isAnimationCacheEnabled(), scheduleLayoutAnimation(), setAnimationCacheEnabled(), and startLayoutAnimation().

AnimationDrawable: Unlike other animation classes this is part of the graphics package. This class is used for implementing the frame by frame animation of drawable objects. However it is just a thin wrapper on top of the animation protocol provided by the Drawable class.

AnimationUtils: Allows you to load animations from xml resource ids.

The role of Transformation Matrices in Animation

The mechanics of animation of are quite simple. Take any scene and if you repaint it quick while some aspect of that scene is changing you get the impression that something is moving smoothly although the repaint is happening only with a certain frequency which is typically 24 or 12 frames per second.

what changes at every redraw may be color, position, orientation, or size or any combination of these. If you want to take a view or a scene or a picture and want to move that scene to the right by a few pixels, you will typically define a transformation matrix and then apply a matrix multiplication or transformation on every pixel of the scene to get the new location. So each tranformation is identified by a certain matrix.

If you start with an identity matrix and then apply each transformation to it then you will get a final matrix that you can apply just one time to transform the view. By changing these matrices in a gradual manner in a time loop you will accomplish animation. Let's spend a little bit more time and understand the matrix api before going further.

You can get an identity matrix by doing


import android.graphics.Matrix;
Matrix matrix = new Matrix();

To achieve rotation you can do


matrix.setRotate(degrees)

This method will rotate the view around origin by those many degrees in a 2D space. Some other methods are


matrix.setScale(x,y);
matrix.setTranslate(x,y);
matrix.setSkew(x,y); 

As you call these methods to arrive at a desired matrix be aware of the "set" semantics. "set" semantics works like a "setting" a variable which means "replace" the current value with the new value. All of these methods, if you follow the java source code of android eventually resolve to some c++ code from a core google graphics library called "skia". You can explore the source code online at


http://android.git.kernel.org/

"git" is a source code control system used by the android open source project. You can learn more about "git" SCM here at


http://git.or.cz/

Now back to understanding of the "set" semantics on these graphics matrices. Let us take a look at the source code for "setScale":


void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
    fMat[kMScaleX] = sx;
    fMat[kMScaleY] = sy;
    fMat[kMPersp2] = kMatrix22Elem;

    fMat[kMTransX] = fMat[kMTransY] =
    fMat[kMSkewX]  = fMat[kMSkewY] = 
    fMat[kMPersp0] = fMat[kMPersp1] = 0;

    this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
}

The scale function replaces all previous scaling values and pretty much resets the matrix and sets the scale. So if you have done anything else to the matrix all that is gone. Let us take a look at the "setTranslate" to see if that is any different.


void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
    if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
        fMat[kMTransX] = dx;
        fMat[kMTransY] = dy;

        fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
        fMat[kMSkewX]  = fMat[kMSkewY] = 
        fMat[kMPersp0] = fMat[kMPersp1] = 0;
        fMat[kMPersp2] = kMatrix22Elem;

        this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
    } else {
        this->reset();
    }
}

Basically sets the scale back to 1 and sets the translations replacing everything before.

So what do you do if you want to apply multiple transformations in a tow. Matrix class provides a set of "pre*" and "post*" functions for each of the "set*" fucnctions. Let us take a look at the source code for "preScale"


bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
    SkMatrix    m;
    m.setScale(sx, sy, px, py);
    return this->preConcat(m);
}

bool SkMatrix::preConcat(const SkMatrix& mat) {
    return this->setConcat(*this, mat);
}

bool SkMatrix::postConcat(const SkMatrix& mat) {
    return this->setConcat(mat, *this);
}

Essentially "preScale" creates a brand new matrix with the given scale and then multiplies that matrix with the current matrix and replaces the current matrix with the resultant matrix. When matrices are multiplied the order is important. So if we do


m.setScale(..) = rm1
m.preScale(..) = m2 x rm1 = rm3
m.postScale(..) = rm3 x m4 = rm5

I could have acheived the same with


m1.setScale() = m1
m2.setScale() = m2
m3.setScale() = m3

m1.setConcat(m2,m1); // rm3
m1.setConcat(m1,m3); // rm5

You can concat

If "m1" was an identity matrix then the following two would be equal as well


m1.postTranslate(); // take it to the origin
m1.postScale(); //scale it
m1.postTranslate();// take it back to the center

m2.setScale();
m2.preTranslate();
m2.postTranslate();

Then the following is a valid assertion


assert(m1.equals(m2));

Here is some more sample code demonstrating matrix equivalence based on the order of operations on that matrix


public class TestMatrix 
{
   public static void test(SomeActivity a)
   {
      test1(a);
      test2(a);
      test3(a);
   }
   public static void test1(SomeActivity a)
   {
      Matrix m1 = new Matrix();
      m1.setScale(2, 2);
      m1.preScale(2, 2);
      m1.postScale(2, 2);
      
      Matrix m2 = new Matrix();
      m2.postScale(2, 2);
      m2.postScale(2, 2);
      m2.postScale(2, 2);

      assertL("test1",a,m1,m2,"m1 and m2 will be same");
   }
   
   public static void test2(SomeActivity a)
   {
      Matrix m1 = new Matrix();
      m1.setScale(2, 2);
      m1.preTranslate(-2, -2);
      m1.postTranslate(2, 2);
      
      Matrix m2 = new Matrix();
      m2.postTranslate(-2, -2);
      m2.postScale(2, 2);
      m2.postTranslate(2, 2);
      
      assertL("test2",a,m1,m2,"m1 and m2 will be the same");
   }
   
   public static void test3(SomeActivity a)
   {
      Matrix m1 = new Matrix();
      m1.setScale(2, 2);
      m1.preTranslate(-2, -2);
      m1.postTranslate(2, 2);
      
      Matrix m2 = new Matrix();
      m2.preTranslate(-2, -2);
      m2.postTranslate(2, 2);
      m2.setScale(2, 2);

      assertL("test4",a,m1,m2,"m1 and m2 will not be the same");

      Matrix m3 = new Matrix();
      m3.setScale(2, 2);
      assertL("test3",a,m2,m3,"m2 and m3 will be the same");
   }
   
   private static void assertL(String testName
         , SomeActivity a
         , Matrix m1
         , Matrix m2
         , String assertText)
   {
      if (m1.equals(m2))
      {
         a.appendText("\n" + testName + ": m1 is same as m2");
      }
      else
      {
         a.appendText("\n" + testName + ": m1 is NOT same as m2");
      }
   }
}

In summary all the "pre*" and "post*" methods are mere shortcuts to "setConcat" against itself.


preTranslate(m) -> setConcat(m x self) 
preRotate(m) -> setConcat(m x self)
preScale(m) -> setConcat(m x self)
preSkew(m) -> setConcat(m x self)

postTranslate(m) -> setConcat (self x m)
postRotate(m) -> setConcat (self x m)
postScale(m) -> setConcat (self x m)
postSkew(m) -> setConcat (self x m)

Layout Animations

Android has rigged some of the animation concepts into layout containers such as list boxes and grids. As a result quickest way to see an animation is to prod a layout container to use a predefined animation in an xml file. The pattern goes as follows

1. Define an animation declaratively in the "anim" sub directory using a file.

2. Android creates an animation id based on the filename for that animation definition

3. Define a spcial case of animation called layout animation which is really a wrapper for the animation defined in step 1. This file is also created in the same location namely "res/anim"

4. Android creates an animation id for this layout animation file as well

5. Specify the animation "id" in step 4 as an animation attribute to the layout in the layout xml file

let us see an example of this by creating a file called "scale.xml" in the "res/anim" sub directory.


//res/anim/scale.xml
<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>

This animation keeps the x scale same, but increases the "y" scale from 1/10 of its size to its target size of "1" (original size). It takes 1/2 a second to reach the target state. It uses mid way point or the center of the view as its pivot point.

Scale attribute may be a floating point value, such as "1.2". May be a fractional value, which is a floating point number appended with either % or %p, such as "14.5%". The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to some parent container. The values can be negative as well.

This animation can be applied to any view as a whole. However it needs to be wrapped in a layout animation tag to be allowed for a layout container. The layout animation declaration allows additional attributes that talk about the collection of items the container manages. Here is an example that uses the above basic animation:


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

As it has been pointed out LayoutAnimation is just a thin wrapper on top of the basic animation. The "30%" delay says that apply the underlying animation after a delay for each contained view. Do the animation in reverse order of items listed in the container. The only remaining attribute of a layout animation that is not listed above is "android:interpolator".

Android defines a set of predefined interpolators. These are


android:interpolator=@anim/accelerate_decelerate_interpolator  
android:interpolator=@anim/accelerate_interpolator  
android:interpolator=@anim/decelerate_interpolator

Each of these definitions are essentially xml files in the base android package under the "res/anim" sub directory.

res/anim/decelerateInterpolator.xml


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

res/anim/accelerateInterpolator.xml


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

res/anim/accelerateDecelerateInterpolator.xml


<accelerateDecelerateInterpolator 
   xmlns:android="http://schemas.android.com/apk/res/android"/>  

Lets talk about interpolators for a minute. An interpolator defines the rate of change in an animation. This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated, decelerated, repeated, etc, sometimes in a particular pattern. An interpolator is used to pass a factor between 0 and 1 to the animation callback through the parameter "interpolatedTime" to the following applyTransformation call back of an Animation object.


@Override
protected void applyTransformation(float interpolatedTime
                          ,Transformation t) 
{
}

The value of "0" for interpolatedTime indicates the start of the animation and the value of "1" indicates the end of animation. One will use this as a multiplication factor to modulate the characteristic that is being animated: be it position or scale or rotation.

Some of the builtin interpolators are

AccelerateDecelerateInterpolator: An interpolator where the rate of change starts and ends slowly but accelerates through the middle. It doesnt have any additional parameters to change the behavior other than what was built into it.

AccelerateInterpolator: An interpolator where the rate of change starts out slowly and and then accelerates. It can take an additional argument called "factor". This argument controls the degree to which the animation should be eased. Seting factor to 1.0f produces a y=x^2 parabola. Increasing factor above 1.0f exaggerates the ease-in effect (i.e., it starts even slower and ends evens faster)

Here is the source code snippet of how "factor" is used


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

By default "mFactor" is 1.

CycleInterpolator: Repeats the animation for a specified number of cycles. The rate of change follows a sinusoidal pattern.

DecelerateInterpolator: It is the opposite of AccelerateInterpolator. It uses the factor the same way but in the opposite direction.

LinearInterpolator: This interpolatro keeps the rate of change constant.

GridLayoutAnimation

LayoutAnimation is typically used for linear lists where there is no concept of rows and columns. For those containers like a grid where there are rows and columns there is a variation on the layoutanimation tag called "GridLayoutAnimation". This tag allows the following additional xml tags as children:

columnDelay: Fraction of the animation duration used to delay the begining of animation for each column. The value is a percentage value.

direction : One of a) left_to_right b) right_to_left c) top_to_bottom d) bottom_to_top

directionPriority: One of a) none b) column c) row. For "none" both columns and rows are animated at the same time. For "Column" columns are animated first. For "row" rows are animated first.

rowDelay: Fraction of the animation duration used to delay the begining of animation for each column. The value is a percentage value.

Both these animations are also available to the java programmers as


LayoutAnimationController
GridLayoutAnimationController

Same level of flexibility is allowed for both these java objects.

Tieing a layout animation to a list control

Let us return to the main line story where we are trying to tie the layout aninmation to a list view. so far we have

1. A basic animation defined in "res/anim/scale.xml"

2. A layout animation that wrapped the basic animation in "res/anim/scale_container_elements.xml"

Let us now look at an xml file for a possible activity. Call this xml file "test_animations_layout.xml"


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <ListView
        android:id="@android:id/list"
        android:persistentDrawingCache="animation|scrolling"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layoutAnimation="@anim/scale_container_elements" />

    <ImageView
        android:id="@+id/picture"
        android:scaleType="fitCenter"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone" />

</FrameLayout>

The frame layout in this example is holding two views. A list view that we will use to point to a list of photo image names and then an image view that will contain the picture of a selected image from the list. Notice how the layout animation attribute is pointing to a scale animation through a layout animation wrapper. Here is a segment of code that can be used to initialize an activity


//Set the activity layout first
setContentView(R.layout.test_animations_layout);

//get the list view
mPhotoListView = (ListView) findViewById(android.R.id.list);

//get an adapter
ArrayAdapter<String> adapter = 
   new ArrayAdapter<String>(this
           ,android.R.layout.simple_list_item_1
           ,PHOTOS_NAMES);

//Notice how it uses a predefined view
//for the list item

//Attach the adapter to the list
mPhotosListView.setAdapter(adapter);

Notice how the list view needs to be supplied with an adapter to populate the rows of the list. This is done through an adapter. Let us see how one can define an array adapter using an array of strings.


private static final 
  String[] PHOTOS_NAMES = new String[] {
        "Lyon",
        "Livermore",
        "Tahoe Pier",
        "Lake Tahoe",
        "Grand Canyon",
        "Bodie"
  };
  
// Resource identifiers for the photos we want to display
private static final int[] PHOTOS_RESOURCES = new int[] {
      R.drawable.photo1,
      R.drawable.photo2,
      R.drawable.photo3,
      R.drawable.photo4,
      R.drawable.photo5,
      R.drawable.photo6
};

This array of strings will become titles for each list item. The adapter also needs the layout view for displaying each item. Although the view for a list item is defined by the "ListView" tag, the view for each list item is controlled by its adapter. Android defines a number of predefined views for these list items. Some of these predefined views include:


simple_list_item_1
simple_list_item_2
simple_list_item_checked
simple_list_item_multiple_choice
simple_list_item_single_choice

The code so far is sufficient to test the scaling animation. When this code is executed you will see that the items in the list box will start out really small and gradually grow to their original size on the "y" axis. Essentially the strings are scaled vertically while keeping their horizontal sizing constant. Now you can change the "scale.xml" to include a variety of animations and see how they effect the list animation. Here are some example animations you can try:

Moving the text items from left to right


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromXDelta="-100%p" 
              android:toXDelta="0" 
            android:duration="150" />
</set>

This is a "translate" animation that moves the object from 100% of the parent size starting off the screen and move to the right and stopping at an "x" value of 0. This has the effect of text sliding to the right and stopping when the left side of the text aligns on the left side with the container.

Just for experimentation you can replace the scale animation in this example with any of the following animations to see how they appear in action. Here are some examples that cover fading, movement, and rotation

An example of fading using "alpha" animation


<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="100" />

Here is how you can move on the Y axis and also fade at the same time


<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="100" />
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="50" />
</set>

Here is an example of rotating the list item at its mid point from 0 to 360 degrees.


<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="100" />

Animating a View as a whole programmatically

So far we have hinted at animating a view using matrices and pointed out that the built in animations like Translate and Scale or just special cases of animating a view with the base class "Animation". We have also showed you how these built in animations can be quickly attached to containers such as list boxes and grids. In the example above we have a ListView that is capable of animating its children. Now we would like to show you how you can animate the ListView itself as a whole. As an added bit of complexity we will rotate the ListView in the "z" axis to the point where it completely disappears when it is turned 90 degrees into the "z" axis.

So we are going to be talking about two concepts. Writing your own animation and then animating something in the third dimension. The android.graphics package which hosts most of these concepts is essentially a 2D package and hence won't allow for a 3D rotation directly through the matirx transformation. Instead it provides an object called "Camera" that applies a perspective view on a 2D object when it is looked from a certain angle including depth perception. So in this example we will also cover the Camera object and how to use it.

This chapter heavily borrows from the animation example provided in the "ApiDemos" but at the same time adds a lot of basic information required to understand that example. In the end you would have covered many of the 2D animation apis in android. This animation example in the api demos essentially does the following. It shows how to tie layout animation to a list box. We have covered this already. At the end of that lay out animation you will see a list of image names displayed in a list box. Once you click on one of the image names, the whole frame will rotate 90 degrees as if in 3D space and the corresponding image for that name will swing in to view using the same animation but in reverse. When you are looking at that image as you click on it, the image will rotate 90 degrees in 3D and disappear and the image name list will appear in reverse animation.

In the process of understanding this exercise you will learn

1. How to write your own animation

2. How to apply 3D like transforms using a Camera object

3. You will know how to apply what you have learned about matrix transformations

4. You will learn about animation call backs and how to use "post" to invoke actions

Creating a specialized Transformation

The structure for specializing an animation is quite simple. You will initialize the rotation with some dimensions both for the object in question and also its parent. then you will override the "often" called applyTranformation. This method is called to simulate the animation using an interpolated time parameter that changes from its initial value to the target value at the end of the animation. The second parameter to this method the "Transformation" essentially is a wrapper to the transformation matrix. Here is the skeleton code for this:


public class Rotate3dAnimation extends Animation 
{
    public Rotate3dAnimation(....){}

    @Override
    public void initialize(int width, int height, 
                  int parentWidth, int parentHeight) 
   {
        super.initialize(width, height, parentWidth, parentHeight);
      ....your stuff
    }

    @Override
    protected void applyTransformation(float interpolatedTime
                                      ,Transformation t) 
    {
    }
}

To reemphasize the simplicity of this idea lets take a quick look at the built-in TranslateAnimation class


public class TranslateAnimation extends Animation {
    private int mFromXType = ABSOLUTE;
    private int mToXType = ABSOLUTE;

    private int mFromYType = ABSOLUTE;
    private int mToYType = ABSOLUTE;

    private float mFromXValue = 0.0f;
    private float mToXValue = 0.0f;

    private float mFromYValue = 0.0f;
    private float mToYValue = 0.0f;

    private float mFromXDelta;
    private float mToXDelta;
    private float mFromYDelta;
    private float mToYDelta;

    public TranslateAnimation(float fromXDelta, float toXDelta, 
                           float fromYDelta, float toYDelta) {
        mFromXValue = fromXDelta;
        mToXValue = toXDelta;
        mFromYValue = fromYDelta;
        mToYValue = toYDelta;

        mFromXType = ABSOLUTE;
        mToXType = ABSOLUTE;
        mFromYType = ABSOLUTE;
        mToYType = ABSOLUTE;
    }

     */
    public TranslateAnimation(int fromXType, float fromXValue, 
        int toXType, float toXValue,
         int fromYType, float fromYValue, int toYType, float toYValue) 
{
        mFromXValue = fromXValue;
        mToXValue = toXValue;
        mFromYValue = fromYValue;
        mToYValue = toYValue;

        mFromXType = fromXType;
        mToXType = toXType;
        mFromYType = fromYType;
        mToYType = toYType;
    }


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
   {
        float dx = mFromXDelta;
        float dy = mFromYDelta;
        if (mFromXDelta != mToXDelta) {
            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
        }
        if (mFromYDelta != mToYDelta) {
            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
        }

        t.getMatrix().setTranslate(dx, dy);
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight)
   {
        super.initialize(width, height, parentWidth, parentHeight);
        mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);
        mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
        mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
        mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
    }
}

Using Matrices and Camera for rotating in 3D on a 2D surface

With the background so far let us examine how we can accomplish 3D like transformation with a set of 2D tools. Let us examine the following code from the api demos closely. we first figure out the amount of degrees to rotate based on the interpolated time. That is straight forward. We then use a "Camera" object. A "Camera" object is provided so that it can manipulate the perspective in a matrix and then give us the resulting matrix. So we start with an empty camera object and then ask the camera to go through a depth perception followed by a rotation on the "Y" axis around the origin. The origin is the left top of a view. Once the rotation on the "Y" axis is complete we get the matrix back from the camera and super impose on the matrix that was gotten from the "Transformation" object. Once the matrix is super imposed we set the Camera back to its original state to reuse it again in the next tranformation cycle.


protected void applyTransformation(float interpolatedTime, 
               Transformation t) 
{
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + 
              ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        camera.save();
        if (mReverse) {
            camera.translate(0.0f, 0.0f, 
                   mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, 
                mDepthZ * (1.0f - interpolatedTime));
        }
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
}

Before we let go of the method we move the view to the origin and then allow the "Camera" transformation and then move it back to its original position. This is often represented by the following pattern


        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);

These two methods are equivalent to moving a view to the center of the 2D geometry and then move back to their original center. This is necessary because the Camera translations did not take a "pivot" point and assume a translation around the origin. With out the "pretranslation" you will see the object move like a fan around the "y" axis instead of a "spin" like motion. Just for your complete understanding you should comment out one of these lines and observe the behavior.

It is also important to note that the rotations on a Camera allows an axis specification where as the Matrix rotations don't specify an axis and assume a 2D world. That is essentially the point of a Camera object.

Let us take a quick look at the methods available on a Camera object.


translate(x,y,z)
rotateX(degrees)
rotateY(degrees)
rotateZ(degrees)

So fundamentally a Camera can translate a view in the third dimension by giving it a perspective (Projection in 2D) and then allows you the same interms of rotation across any axis.

Making 3D rotation a reusable animation object

Here is the constructor of the 3D rotation animation that any code can use to construct


public Rotate3dAnimation(float fromDegrees, float toDegrees,
            float centerX, float centerY, 
         float depthZ, boolean reverse)

The from and to degrees allow how much rotation around the center of "x" and "y" at how much depth and if the rotation should be in reverse.

Here is an example of how this object is constructed and used in the demo appplication


private void applyRotation(View view, int position, 
               float startAngle, float endAngle) 
{
        // Find the center of the container
        final float centerX = view.getWidth() / 2.0f;
        final float centerY = view.getHeight() / 2.0f;

        // Create a new 3D rotation with the supplied parameter
        // The animation listener is used to trigger the next animation
        final Rotate3dAnimation rotation =
                new Rotate3dAnimation(startAngle, endAngle, centerX, 
                     centerY, 310.0f, true);
        rotation.setDuration(500);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());
      
        rotation.setAnimationListener(new DisplayNextView(position));

        view.startAnimation(rotation);
}

Here are two places where this applyRotation is called


applyRotation(someView, position, 0, 90);
applyRotation(someView, -1, 180, 90);

Coding Animation Callbacks

The variable "position" is unrelated to animation and it is used here to know which item in the list box is selected. Depending on the item that is selected a new image corresponding to that position is brought into view using the 3D animation. Another notable takeaway from the above code is how a call back is registered when the animation completes. Let us take a look at that code as that will demonstrate how animation call backs are used


    private final class DisplayNextView implements Animation.AnimationListener {
        private final int mPosition;

        private DisplayNextView(int position) {
            mPosition = position;
        }

        public void onAnimationStart(Animation animation) {
        }

        public void onAnimationEnd(Animation animation) {
            mContainer.post(new SwapViews(mPosition));
        }

        public void onAnimationRepeat(Animation animation) {
        }
    }

Once again in the call back the necessary action is deferred and invoked using a post back so that it can be executed on the main UI thread and allowing the animation to come to a conclusion. Let us see how that call back code looks like:


private final class SwapViews implements Runnable {
   private final int mPosition;

   public SwapViews(int position) {
      mPosition = position;
   }

   public void run() {
      final float centerX = mContainer.getWidth() / 2.0f;
      final float centerY = mContainer.getHeight() / 2.0f;
      Rotate3dAnimation rotation;
      
      if (mPosition > -1) {
         mPhotosList.setVisibility(View.GONE);
         mImageView.setVisibility(View.VISIBLE);
         mImageView.requestFocus();

         rotation = new Rotate3dAnimation(90, 180, centerX, centerY, 310.0f, false);
      } else {
         mImageView.setVisibility(View.GONE);
         mPhotosList.setVisibility(View.VISIBLE);
         mPhotosList.requestFocus();

         rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
      }

      rotation.setDuration(500);
      rotation.setFillAfter(true);
      rotation.setInterpolator(new DecelerateInterpolator());

      mContainer.startAnimation(rotation);
   }
}

The key to note is that the call back for a "post" method is a Runnable object with a "run" method. In this case it sets the visible component of a FrameView from the list to the individual image and vice versa. And for that reason the class is called "swapviews". A framelayout essentially allows visibility to one view at a time. Once the views are swapped it applies 3D rotation on those views.

Starting Animation

We now know how to swap the views between list view and the image view at the end of an animation. Let us see now how we respond to the mouse clicks on these views. For example the mouse click on a list item should trigger an animation to show the image. A mouse click on the image should trigger an animation to show the list view. Here are these two call back functions:


public class Transition3d extends Activity implements
        AdapterView.OnItemClickListener, View.OnClickListener 
{
   .....
    public void onItemClick(AdapterView parent, View v, int position, long id) 
   {
        // Pre-load the image then start the animation
        mImageView.setImageResource(PHOTOS_RESOURCES[position]);
        applyRotation(position, 0, 90);
    }

    public void onClick(View v) {
        applyRotation(-1, 180, 90);
    }
   ....
}

Notice how we have to implement two kinds of callbacks. One on the AdapterView and another on a View. See how each call back is reusing the common applyRotation function.

Example XML demonstrating animation

It should be clear by now that crafting your own animations is simply a matter of changing the transformation matrix in the animation call back on a view. It should be no surprise that all of the built in animations are constructed this way. Here is the relevent source code for some of these built in animations:

Rotate Animation


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);

        if (mPivotX == 0.0f && mPivotY == 0.0f) {
            t.getMatrix().setRotate(degrees);
        } else {
            t.getMatrix().setRotate(degrees, mPivotX, mPivotY);
        }
    }

Scale Animation


@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
   float sx = 1.0f;
   float sy = 1.0f;

   if (mFromX != 1.0f || mToX != 1.0f) {
      sx = mFromX + ((mToX - mFromX) * interpolatedTime);
   }
   if (mFromY != 1.0f || mToY != 1.0f) {
      sy = mFromY + ((mToY - mFromY) * interpolatedTime);
   }

   if (mPivotX == 0 && mPivotY == 0) {
      t.getMatrix().setScale(sx, sy);
   } else {
      t.getMatrix().setScale(sx, sy, mPivotX, mPivotY);
   }
}

Alpha Animation


@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
   final float alpha = mFromAlpha;
   t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}

The simplicity of this approach should give us confidance to craft our own animations quite easily. Let us now turn our attention to more practical matters and take in some sample animation XML files so that we could quickly use these XML animation patterns in our own code. These examples are borrowed from Android documentation


<set android:shareInterpolator="true" 
     android:interpolator="@android:anim/accelerate_interpolator">

    <translate android:fromXDelta="0"
               android:toXDelta="30"
               android:duration="800"
               android:fillAfter="true"/>
    
    <set android:duration="800" 
         android:pivotX="50%"
         android:pivotY="50%" >

        <rotate android:fromDegrees="0"
                android:toDegrees="-90" 
                android:fillAfter="true"
                android:startOffset="800"/>
    
        <scale android:fromXScale="1.0"
                android:toXScale="2.0"
                android:fromYScale="1.0"
                android:toYScale="2.0"
                android:startOffset="800" />
    </set>

    <translate android:toYDelta="-100"
               android:fillAfter="true"
               android:duration="800"
               android:startOffset="1600"/>
</set>

Example Java code

Equivalent java code for the same is


// Create root AnimationSet.
AnimationSet rootSet = new AnimationSet(true);
rootSet.setInterpolator(new AccelerateInterpolator());
rootSet.setRepeatMode(Animation.NO_REPEAT);

// Create and add first child, a motion animation.
TranslateAnimation trans1 = new TranslateAnimation(0, 30, 0, 0);
trans1.setStartOffset(0);
trans1.setDuration(800);
trans1.setFillAfter(true);
rootSet.addAnimation(trans1);

// Create a rotate and a size animation.
RotateAnimation rotate = new RotateAnimation(
       0, 
       -90, 
       RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
       RotateAnimation.RELATIVE_TO_SELF, 0.5f);
       rotate.setFillAfter(true);
       rotate.setDuration(800);

ScaleAnimation scale = new ScaleAnimation(
       1, 2, 1, 2, // From x, to x, from y, to y
       ScaleAnimation.RELATIVE_TO_SELF, 0.5f, 
       ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
       scale.setDuration(800);
       scale.setFillAfter(true);

// Add rotate and size animations to a new set,
// then add the set to the root set.
AnimationSet childSet = new AnimationSet(true);
childSet.setStartOffset(800);
childSet.addAnimation(rotate);
childSet.addAnimation(scale);
rootSet.addAnimation(childSet);

// Add a final motion animation to the root set.
TranslateAnimation trans2 = new TranslateAnimation(0, 0, 0, -100);
trans2.setFillAfter(true);
trans2.setDuration(800);
trans2.setStartOffset(1600);
rootSet.addAnimation(trans2);

// Start the animation.
animWindow.startAnimation(rootSet);

Standard Animation Attributes

android:startOffset: The start time (in milliseconds) of a transformation, where 0 is the start time of the root animation set.

android:duration: The duration (in milliseconds) of a transformation.

android:fillafter: Whether you want the transform you apply to continue after the duration of the transformation has expired. If false, the original value will immediately be applied when the transformation is done. So, for example, if you want to make a dot move down, then right in an "L" shape, if this value is not true, at the end of the down motion the text box will immediately jump back to the top before moving right.

android:fillBefore: True if you want this transformation to be applied at time zero, regardless of your start time value (you will probably never need this).

Dimensions in Android

Consider the following translate animation XML specification


<translate android:fromXDelta="-100%p" 
android:toXDelta="2" 
android:duration="3000" 
android:fillAfter="true"/>

One will wonder what is allowed as a dimension in the "toXDelta". Currently it has "2". What is 2? Is it one of the following dimensions that are allowed in Android:

px - pixels on the screen

in - inches

mm - millimeters

pt - points 1/72 of an inch

dp - density independent pixels. Relative to a 160 dpi screen.

sp - Scale-independent Pixels. These are like the dp units, but it are also scaled by the user's font size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted for both the screen density and user's preference.

Examples of dimensions.


<resources>
    <dimen name="one_pixel">1px</dimen>
    <dimen name="double_density">2dp</dimen>
    <dimen name="sixteen_sp">16sp</dimen>
</resources>

As it turns out if you replace "2" with a "2sp" or "2px" you will get a compile time error indicating that dimension values are not allowed for these attributes. So what are these values then?

These attributes supports values in any of the following three formats: values from -100 to 100, ending with "%", indicating a percentage relative to itself; values from -100 to 100, ending in "%p", indicating a percentage relative to its parent; a float with no suffix, indicating an absolute value.

Consider the example below


<translate android:fromXDelta="-100%p" 
android:toXDelta="150" 
android:duration="1000" 
android:fillAfter="true"/>

Meaning: Start at an "X" value that starts at negative 100% of the parent and move to positive 150 pixels (the left side coordinate of the object) and take 1 sec to do that. For example if what you were animating is text, then the text will start at right most point and move to the right until the left hand side of the text moved 150 pixels to the right from the left 0.

A better translation is this


<translate android:fromXDelta="-100%p" 
android:toXDelta="0" 
android:duration="3000" 
android:fillAfter="true"/>

This stops the animation as soon as the left most letter comes into the visibility as the word moves to the right. Once the animation is complete, the text goes back to its original state of X:0. This would be the same as the final state of the animation. Otherwise you will see a sudden shift from the final state of the animation to the natural positioning of the object.

Here is an example of scale animation that scales the text on "Y" axis for 1/10th to its original size over half a second.


<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" />

Source code of interpolators

To understand interpolators well and what they do underneath here is the source code of the predefined interpolators in Android.

LinearInterpolator: Uses a standard linear gradient where the rate of change is constant


public float getInterpolation(float input) 
{
   return input;
}

CycleInterpolator: Uses a sinusoidal pattern for a given number of cycles.


public CycleInterpolator(float cycles) 
{
   mCycles = cycles;
}
public float getInterpolation(float input) {
   return (float)(Math.sin(2 * mCycles * Math.PI * input));
}

AccelerateInterpolator: Uses a hyperbolic curve to interpolate amplitude values along the animation curve.


public AccelerateInterpolator(float factor) {
   mFactor = factor;
}

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

DecelerateInterpolator: The complement of the AccelerateInterpolator.


public DecelerateInterpolator(float factor) {
   mFactor = factor;
}

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

AccelerateDecelerateInterpolator: The rate of change starts and ends slowly but accelerates through the middle.


public float getInterpolation(float input) {
   return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

Frame By Frame Animation

Frame By Frame Animation is accomplished through a class in the graphics package called AnimationDrawable. This class is capable of taking a list of drawable resources and then render them at the specified intervals. This class is really a thin wrapper to the animation support provided by the basic Drawable class. Drawable class enables animation by asking its container (or on which it draws such as say a View) to invoke a runnable class that essentailly redraws the drawable using a different set of parameters. However these internal implementation details are not necessary to use the AnimationDrawable class. To use this class start with a set of drawable resources placed in the "res/drawable" sub directory. Here is an example


<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
   android:oneshot="false">
    <item android:drawable="@drawable/numbers11" android:duration="50" />
    <item android:drawable="@drawable/numbers12" android:duration="50" />
    <item android:drawable="@drawable/numbers13" android:duration="50" />
    <item android:drawable="@drawable/numbers14" android:duration="50" />
    <item android:drawable="@drawable/numbers15" android:duration="50" />
    <item android:drawable="@drawable/numbers16" android:duration="50" />
    <item android:drawable="@drawable/numbers17" android:duration="50" />
    <item android:drawable="@drawable/numbers18" android:duration="50" />
    <item android:drawable="@drawable/numbers19" android:duration="50" />
 </animation-list> 

I have called this file "res/drawable/frame_animation.xml". These are png images that I have created using a power point. I drew a big circle and then placed a small ball on the perimeter of the circle in 8 places. Then I saved the images 9 time with each image having the ball at a different place on the circular line. The goal is to play these images in succession to show as if the ball is moving in a circular orbit.

Here is how I have defined a layout to test this.


<?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"
    >
<Button 
   android:id="@+id/startFAButtonId"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Start Animation"
/>

<ImageView
        android:id="@+id/animationImage"
        android:layout_width="fill_parent"
       android:layout_height="wrap_content" 
        />
</LinearLayout>

The layout is basically a button followed by an image view. The image view will be used as the background for the animation. Let us see how the activity is going to be initialized.


public class FrameAnimationActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.frame_animations_layout);
        this.setupButton();
    }
....
}

Pretty clear so far. Just initialize the activity and call to setup the button to start the animation. Here is the code for "setupButton":


private void setupButton()
{
   Button b = (Button)this.findViewById(R.id.startFAButtonId);
   b.setOnClickListener(
       new Button.OnClickListener(){
         public void onClick(View v)
         {
            animateImageView();
         }
       });
}

The animation is quite simple as well. When you set the background resource of an image using an "animation_list" drawable it creates an AnimationDrawable and attaches it as the drawable resource of the background. We get this object and start animation on it. A few important caveats however. For testing this animation don't set the resource for the image itself. The "z" order seem to mask the animation. Also if you want to restart the animation after you have started it, you will have to "stop" and then "start". If you allow the animation to repeat then this is not a concern, clearly. The animation below is coded in such a way that if you press the button it will start the animation. And if you press the button again it will stop and the pattern continues between start and stop.


private void animateImageView()
{
   // Load the ImageView that will host the animation and
    // set its background to our AnimationDrawable XML resource.
    ImageView img = (ImageView)findViewById(R.id.animationImage);
    img.setBackgroundResource(R.drawable.frame_animation);

    // Get the background, which has been compiled to an AnimationDrawable object.
    AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();

    // Start the animation (looped playback by default).
    if (frameAnimation.isRunning())
    {
       frameAnimation.stop();
    }
    else
    {
       frameAnimation.start();
    }
}

Unlike the Animation class this AnimationDrawable class does not provide a callback on the completion of the animation. For example if you set the "oneshot" parameter of the animation list to "true" then the animation will stop after doing the animation once. However there is no clearcut way to know that.

There is more to Android animation. The leaf level classes of animation such as those derived from Animation (ScaleAnimation, TranslateAnimation etc) and those derived from Drawable (AnimationDrawable) or just the tip of the animation facilities. The core of animation lies in redrawing the canvas using a combination of these facilities that are outlined here.

Sample Animation Files

Layout animations combined with XML animation definitions provide a powerful way to quickly add visual effects to Android applications. As you start experimenting with animations it is worthwhile to keep a quick reference to example animations so that you can copy the code and change the code for your own need. In that spirit here are some example animations from the android documentation. These will be handy as you work the animations in Android.

Fade.xml


<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="100" />

layout_grid_fade.xml


<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:rowDelay="50%"
        android:directionPriority="column"
        android:animation="@anim/fade" />   

layout_grid_inverse_fade.xml


<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:columnDelay="0.5"
        android:directionPriority="row"
        android:direction="right_to_left|bottom_to_top"
        android:animation="@anim/fade" />      

layout_grid_random_fade.xml


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="0.5"
        android:animationOrder="random"
        android:animation="@anim/fade" />

hyperspace_in.xml


<alpha xmlns:android="http://schemas.android.com/apk/res/android" 
android:fromAlpha="0.0" android:toAlpha="1.0" 
android:duration="300" android:startOffset="1200" />

hyperspace_out.xml


<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
   <scale 
      android:interpolator="@android:anim/accelerate_decelerate_interpolator"
      android:fromXScale="1.0" 
      android:toXScale="1.4" 
      android:fromYScale="1.0" 
      android:toYScale="0.6" 
      android:pivotX="50%"
      android:pivotY="50%"
      android:fillAfter="false"
      android:duration="700" />
   <set 
      android:interpolator="@android:anim/accelerate_interpolator"
                android:startOffset="700">
      <scale
         android:fromXScale="1.4" 
         android:toXScale="0.0"
              android:fromYScale="0.6"
          android:toYScale="0.0" 
          android:pivotX="50%" 
          android:pivotY="50%" 
          android:duration="400" />
      <rotate 
         android:fromDegrees="0" 
         android:toDegrees="-45"
          android:toYScale="0.0" 
          android:pivotX="50%" 
          android:pivotY="50%"
          android:duration="400" />
   </set>
</set>

layout_animation_row_left_slide.xml


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="10%"
        android:animation="@anim/slide_left" />

layout_animation_row_right_slide.xml


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

layout_animation_table.xml


<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="50%"
        android:animation="@anim/slide_top_to_bottom" />

layout_animation_bottom_to_top.xml


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

layout_wave_scale.xml


<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:rowDelay="75%"
        android:columnDelay="0%"
        android:directionPriority="none"
        android:animation="@anim/wave_scale" />

wave_scale.xml


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="100" />
    <scale
        android:fromXScale="0.5" android:toXScale="1.5"
        android:fromYScale="0.5" android:toYScale="1.5"
        android:pivotX="50%" android:pivotY="50%"
        android:duration="200" />
    <scale 
        android:fromXScale="1.5" android:toXScale="1.0"
        android:fromYScale="1.5" android:toYScale="1.0"
        android:pivotX="50%" android:pivotY="50%"
        android:startOffset="200"
        android:duration="100" />
</set>

push_left_in.xml


<set xmlns:android="http://schemas.android.com/apk/res/android">
   <translate android:fromXDelta="100%p" android:toXDelta="0" 
         android:duration="300"/>
   <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
         android:duration="300" />
</set>      

push_left_out.xml


<set xmlns:android="http://schemas.android.com/apk/res/android">
   <translate android:fromXDelta="0" android:toXDelta="-100%p" 
        android:duration="300"/>
   <alpha android:fromAlpha="1.0" android:toAlpha="0.0" 
        android:duration="300" />
</set>

push_up_in.xml


<set xmlns:android="http://schemas.android.com/apk/res/android">
   <translate android:fromYDelta="100%p" android:toYDelta="0" 
          android:duration="300"/>
   <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
          android:duration="300" />
</set>

push_up_out.xml


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

cycle_7.xml


<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" 
android:cycles="7" />

shake.xml


<translate xmlns:android="http://schemas.android.com/apk/res/android" 
android:fromXDelta="0" android:toXDelta="10" 
android:duration="1000" android:interpolator="@anim/cycle_7" />

slide_left.xml


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
   <translate android:fromXDelta="100%p" android:toXDelta="0" 
        android:duration="150" />
</set>

slide_right.xml


<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromXDelta="-100%p" android:toXDelta="0" 
       android:duration="150" />
</set>

slide_top_to_bottom.xml


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