Introduction

In Android Animation Part 1 we started to examine animation capabilities within Android using the capabilities defined in the android.view.animation package. These include tween and drawable based animations. Both of these have been in Android since API level 1 and provide basic animation support. Creating custom animations is straightforward for both, but have their limitations. With drawables you have a series of pictures which are shown in sequence, like an animated film. Using tween animations you can alter the way a view (and its hierarchy) is drawn by transforming certain characteristics. This is great for simple things, but shows its limitations once you really start working with it.

In order to allow more advanced functionality, Android received an update to its animation support in Honeycomb (API 11) with the introduction of the android.animation package. The package adds a more generic Animator base class, some versatile child classes and a highly customizable mechanism for evaluating changes. Let’s take a closer look at this package, how it compares to the view based animations and how to use it.

 

Animator

At the core of the android.animation package is the Animator base class. This base class gives us a common set of APIs for setting and getting parameters for the animation, such as the duration. It also defines the APIs used to start, stop, pause and resume the animation. Additionally, it defines an inner AnimatorListener interface which is used to callback registered listeners during these animation points, similar to the listener interface used by the Animation class. The vast majority of the methods defined by the Animator class are abstract and must be implemented by the subclasses. When using an Animator at this high level, the listener interface is critical for your code to be notified of operational changes.

There are two direct subclasses of Animator in the framework: AnimatorSet and ValueAnimator. The AnimatorSet class allows you to build up multiple Animators to be run in parallel or serially, similar to what we did with Animation classes in the previous article in this series. The ValueAnimator is where we really get going.

A ValueAnimator is used to animate between a set of values over a period of time. Sounds rather nebulous, right? It is! This class is extremely generic in nature, which lends it to be used for just about anything. What the values are is up to you. It could be an int, a float or even an Object of some kind. Unlike an Animation, the ValueAnimator does not explicitly alter how a View is drawn. It is up to you, the developer using the ValueAnimator to decide how to use the new value and to actually apply it. How do you know when to apply the new value? You register a separate lister, the ValueAnimator.AnimatorUpdateListener, to be notified when a frame is occurring. A quick example will help solidify the concept.

 

public class SampleAnimatorActivity extends Activity
                                    implements ValueAnimator.AnimatorUpdateListener {
    ...
    private Button         mAnimLogButton;
    private ValueAnimator  mAnim;

    @Override
    protected void onCreate(Bundle savedInstance) {
        ...
        mAnim = ValueAnimator.ofFloat(0.0f, 1.0f);
        mAnim.setDuration(2000);
        mAnim.addUpdateListener(this);
        mAnimLogButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                mAnim.start();
            }
        });
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.d(TAG,
              "Update fraction: " +
                  animation.getAnimatedFraction() +
                  " value: " +
                  animation.getAnimatedValue());
    }
}

During the onCreate call, a new ValueAnimator object is created for a float ranging from 0.0 to 1.0 over a 2 second period of time. The Activity is also set to be the animator’s update listener. The animation is started in when a button is clicked and all the listener does is log the animated value. This simple example shows the basic programmatic use of a ValueAnimator, but it also clearly illustrates how the Animator objects are not explicitly tied to View objects. Note that like the Animation, the Animator creates its transformed values using an Interpolator. So the same Interpolators we examined before (or a custom one you have created) can still be leveraged.

 

Animator vs. Animation

Above we saw a simple use of an animator which did nothing more than log the animated value rather than update the UI. Why? In order to illustrate the generic nature of the Animator, which is what we’ll used to change the View‘s characteristics. Many of the examples you’ll find in the documentation, as well as in this article, will show you how to use Animators to create motion or change to your UI. Let’s face it, that’s really what we are after. The difference is that with the Animation class its transform is applied by the framework when the View is drawn. An Animator, on the other hand, is used to alter the characteristic of the view over the course of the animation.

With tween animation, the changes are applied on VSYNC if the view needs to be drawn (e.g. it has been invalidated). The system’s VSYNC triggers the Choreographer object tied to the UI thread’s looper to be run and any views to be drawn are walked. When there is an activate animation on a View, it is processed as part of the View‘s onDraw(). That view will notify the parent that a transformation is in play and it will cause the View (and all of its children) to be re-drawn using the transformed value(s).

When using an Animator, the process is a bit different. When an Animator is started, it is posted to the Choreographer object on the UI thread directly. When the system VSYNC occurs and the Choreographer runs, all Animators are processed before View traversal happens. This means that the Animator has an opportunity to change a property of a View (or something else) before the current View hierarchy is walked and processed. Not only is this more efficient, but it is also much more flexible. Using an Animator, you can adjust any property or characteristic of a View or even another object. In fact, there is another helper class which can be used to do just that: the ObjectAnimator.

 

ObjectAnimator

The ObjectAnimator class is another generic class used to operate on other objects. It builds upon the ValueAnimator, but always has a “target” object. Notice I used “object” and not “View“. With an ObjectAnimator, you specify the target object and the property name you wish to alter over the course of the animation. This class leverages reflection to automatically get the setter and getter methods for the specified property. Of course, that also means there is a convention to be followed by the target. The target class must provide setter and getter methods which start with “set” and “get” then use a camel case version of the property name. For example, if we want to use an ObjectAnimator to fade in a text field, we can create it with the TextView target and a property of “alpha“. The View base object has an alpha property which is wrapped by setAlpha() and getAlpha() calls.

public class SampleAnimatorActivity extends Activity
                                    implements ValueAnimator.AnimatorUpdateListener {
    ...
    private ObjectAnimator  mAnimFadeOut;
    private ObjectAnimator  mAnimFadeIn;
    private Button          mAnimFadeButton;
    private TextView        mAnimFadeText;

    @Override
    protected void onCreate(Bundle savedInstance) {
        ...
        mAnimFadeOut =
            ObjectAnimator.ofFloat(mAnimFadeText, "alpha", 1.0f, 0.0f);
        mAnimFadeIn =
            ObjectAnimator.ofFloat(mAnimFadeText, "alpha", 0.0f, 1.0f);
        mAnimFadeOut.setDuration(1000);
        mAnimFadeIn.setDuration(1000);
        mAnimFadeButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mAnimFadeText.getAlpha() > 0.5f) {
                    mAnimFadeOut.start();
                } else {
                    mAnimFadeIn.start();
                }
            }
        });
    }
}

 

Using XML

Of course, like most other building blocks in Android, you can use XML to define your Animator based animations. This gives us a lot of flexibility and re-usability for our animations. Like the Animation XML, you can also use this to define a set of animations to be run together serially or in parallel. This can get a little confusing if you’re not careful, so here are a couple of helpful points:

  • XML definitions for Animator type animations go in the res/animator subdirectory of your project. When using XML to define Animation type animations, those go in res/anim beneath your project tree.
  • The tag creates ValueAnimator since that is the most basic, usable form of an Animator object.
  • A <set> in the XML files used to define Animator based animations creates an AnimatorSet object when inflated. Sets can contain other sets.
  • Note that both Animator and Animation XML definitions can use the <set> tag! This means the resource location naming is critical, so refer to the first bullet above!

Rounding out our simple test application, a 3rd button is present which leverages an XML based AnimatorSet to flip a text field with a little fade.

 

Wrap Up

Animators provide an extremely extensible way to animate any type of object within your app. However, it does have some limitations. Most notably, it is not available for every type of situation. The best examples are Fragment transitions and Activity start/end animations. For these you still have to use tween based animations unless you get creative with your lifecycle transition handling, which can be disastrous if you are not careful.

 
There are additional features available for use with Animators, which we’ll explore in the next installment of this Android animation series. Until then, experiment and enjoy!

 

References

[1] SampleAnimator app: https://github.com/hiq-larryschiefer/SampleAnimator.git