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 Animator
s 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 Interpolator
s 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 Animator
s 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 Animator
s 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 theres/animator
subdirectory of your project. When using XML to defineAnimation
type animations, those go inres/anim
beneath your project tree. - The tag creates
ValueAnimator
since that is the most basic, usable form of anAnimator
object. - A
<set>
in the XML files used to defineAnimator
based animations creates anAnimatorSet
object when inflated. Sets can contain other sets. - Note that both
Animator
andAnimation
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
[…] Previous […]