Tuesday, January 17, 2012

Custom Android UI animation (TranslateAnimation, RotateAnimation or AlphaAnimation)


Animations are a great way to spice up the UI of your app. Although using too many animations can freak out the user, applying one or two in the right places can make the interface look professional and add some points to the user experience.
Most developers have no problem learning and using the standard animations that Android provides, such as TranslateAnimationRotateAnimation or AlphaAnimation. You can specify them both in XML and programmatically in the code. Also, it's easy to understand the Interpolators - linear, cycle, acceleration/deceleration etc.
What could also be cool for your app is to have a custom, unique animation that is not seen in any other app. Fortunately, that is easy enough to do. In this article, we will develop a simple custom animation and apply it to a UI element to make a demo.

App Layout

We'll use a simple app layout described by the following 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">
  <ImageView
   android:id="@+id/image"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center"
   android:layout_marginTop="30dip"
   android:src="@drawable/rhino" />
  <Button
   android:id="@+id/button"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center"
   android:layout_marginTop="30dip"
   android:text="Please click me" />
</LinearLayout>



We expect the image to animate with our custom animation when the user clicks the button.

Coding the animation

For simplicity, let's make an animation that only modifies the alpha value (transparency) and does not change the transformation matrix (scaling, translation, rotation etc.). The standardAlphaAnimation is a good example. However, it is too simple. Let's make an animation that doesn't simply fade our view in or out, but instead makes the view blink multiple times by modifying its transparency.
In order to create our custom animation, let's just subclass the Animation class:
import android.util.FloatMath;
import android.view.animation.Animation;
import android.view.animation.Transformation;


public class BlinkAnimation extends Animation {
/*...*/
Now, curiously, the Animation class does not have any abstract methods that are required to be overridden. However, for most animations you will need to override the applyTransformation()method. That method actually defines the animation by taking the interpolated time and thetransformation object as inputs and modifying the transformation object according to the time and the animation logic. In our case, the method will change the transparency by a cosine function that we loop, shift and take the absolute value of, according to the settings our animation object has:
public class BlinkAnimation extends Animation {
    private int totalBlinks;
    private boolean finishOff;
   
    public BlinkAnimation(int totalBlinks, boolean finishOff) {
        this.totalBlinks = totalBlinks;
        this.finishOff = finishOff;
    }


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float period = interpolatedTime * totalBlinks * 3.14f + (finishOff ?3.14f / 2 : 0);
        t.setAlpha(Math.abs(FloatMath.cos(period)));
    }


/*...*/
The totalBlinks parameter defines the total number of times our view will blink. The finishOffparameter defines whether the animation will finish with a fade-out or with a fade-in. This is good if we want to show or hide the view after the animation has finished.
It is very important to understand that interpolatedTime is a value between 0.0f and 1.0f that represents an artificial, non-linear timeline. That is, unless you use a LinearInterpolator(See this for other Interpolator), theinterpolatedTime value will not change linearly with time, but will change with an acceleration, or in cycle, or with acceleration+deceleration etc. This allows to combine the animation effect with various time effects that make the animation look more interesting.
In addition, we override the following methods to hint the animation engine that our animation does not change the transformation matrix or the bounds of the view:
    @Override
    public boolean willChangeBounds() {
        return false;
    }


    @Override
    public boolean willChangeTransformationMatrix() {
        return false;
    }

Using the animation

Although we defined a very simple animation using only few trivial lines of code, we can now use the entire animation framework with our animation, such as interpolators, duration changes and animation listeners. Here's the code of the activity that uses our new BlinkAnimation:
public class TestActivity extends Activity implements OnClickListener, AnimationListener {
    private ImageView image;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        image = (ImageView) findViewById(R.id.image);
        ((Button) findViewById(R.id.button)).setOnClickListener(this);
    }


    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.button) {
            Animation animation;
            if (image.getVisibility() == View.VISIBLE) {
                animation = new BlinkAnimation(3true);
                animation.setInterpolator(new DecelerateInterpolator());
            } else {
                animation = new BlinkAnimation(3false);
                animation.setInterpolator(new AccelerateInterpolator());
            }
            animation.setDuration(1000L);
            animation.setAnimationListener(this);
           
            image.startAnimation(animation);
        }
    }


    @Override
    public void onAnimationEnd(Animation animation) {
        image.setVisibility(image.getVisibility() == View.VISIBLE ?
                View.INVISIBLE : View.VISIBLE);
    }


    @Override
    public void onAnimationRepeat(Animation animation) {}


    @Override
    public void onAnimationStart(Animation animation) {}
}
As you can see, we use the animation to toggle the image on and off, and we use AccelerationInterpolator and DecelerationInterpolator to make our animation look more natural.
I recommend you to grab the source below and try this animation in your own app. Then try to make something more complex and interesting. There is a lot of room for your creativity with custom Android animations.


No comments:

Post a Comment