2.2. Background animation
Frame-by-frame
animation, as it is called in the Google documentation, is
completely straightforward: a set of frames, played in order at
regular intervals. This kind of animation is implemented by subclasses
of AnimationDrawable.
As subclasses of Drawable,
AnimationDrawable objects can be
used in any context that any other Drawable is used. The mechanism that
animates them, however, is not a part of the Drawable itself. In order to animate, an
AnimationDrawable relies on an
external service provider—an implementation of the Drawable.Callback interface—to animate
it.
The View class implements
this interface and can be used to animate an AnimationDrawable. Unfortunately, it will
supply animation services only to the one
Drawable object that is installed
as its background with one of the two methods setBackgroundDrawable or setBackgroundResource.
The good news, however, is that this is probably sufficient. A
background animation has access to the entire widget canvas.
Everything it draws will appear to be behind anything drawn by the
View.onDraw method, so it would be
hard to use the background to implement full-fledged sprites
(animation integrated into a static scene). Still, with clever use of
the DrawableContainer class (which
allows you to animate several different animations simultaneously) and
because the background can be changed at any time, it is possible to
accomplish quite a bit without resorting to implementing your own
animation framework.
An AnimationDrawable in a
view background is entirely sufficient to do anything from, say,
indicating that some long-running activity is taking place—maybe
winged packets flying across the screen from a phone to a tower—to
simply making a button’s background pulse.
The pulsing button example is illustrative and surprisingly easy
to implement. Examples Example 5 and Example 6 show all you need. The animation is
defined as a resource, and code applies it to the button.
Example 5. Frame-by-frame animation (resource)
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/throbber_f0" android:duration="70" /> <item android:drawable="@drawable/throbber_f1" android:duration="70" /> <item android:drawable="@drawable/throbber_f2" android:duration="70" /> <item android:drawable="@drawable/throbber_f3" android:duration="70" /> <item android:drawable="@drawable/throbber_f4" android:duration="70" /> <item android:drawable="@drawable/throbber_f5" android:duration="70" /> <item android:drawable="@drawable/throbber_f6" android:duration="70" /> </animation-list> |
Example 6. Frame-by-frame animation (code)
// w is a button that will "throb" button.setBackgroundResource(R.drawable.throbber);
//!!! This is necessary, but should not be so in Cupcake button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { AnimationDrawable animation = (AnimationDrawable) v.getBackground(); if (animation.isRunning()) { animation.stop(); } else { animation.start(); } // button action. } }); |
There are several gotchas here, though. First of all, as of this
writing, the animation-list example
in the Google documentation does not quite work. There is a problem
with the way it identifies the animation-list resource. To make it work,
don’t define an android:id in that
resource. Instead, simply refer to the object by its filename
(R.drawable.throbber), as Example 6
demonstrates.
The second issue is that a bug in the V1_r2 release of the
toolkit prevents a background animation from being started in the
Activity.onCreate method. If your
application’s background should be animated whenever it is visible,
you’ll have to use trickery to start it. The example implementation
uses an onClick handler. There are
suggestions on the Web that the animation can also be started
successfully from a thread that pauses briefly before calling AnimationDrawable.start. The Android
development team has a fix for this problem, so the constraint should
be relaxed with the release of Cupcake.
Finally, if you have worked with other UI frameworks, especially
Mobile UI frameworks, you may be accustomed to painting the view
background in the first couple of lines of the onDraw method (or equivalent). If you do
that in Android, however, you will paint over your animation. It is,
in general, a good idea to get into the habit of using setBackground to control the View background, whether it is a solid
color, a gradient, an image, or an animation.
Specifying an AnimationDrawable by resource is very
flexible. You can specify a list of drawable resources—any images you
like—that comprise the animation. If your animation needs to be
dynamic, AnimationDrawable is a straightforward
recipe for creating a dynamic drawable that can be animated in the
background of a View.
2.3. Surface view animation
Full-on animation requires a SurfaceView. The SurfaceView provides a node in the view tree
(and, therefore, space on the display) on which any process at all can
draw. The SurfaceView node is laid
out, sized, and receives clicks and updates, just like any other
widget. Instead of drawing, however, it simply reserves space on the
screen, preventing other widgets from affecting any of the pixels
within its frame.
Drawing on a SurfaceView
requires implementing the SurfaceHolder.Callback interface. The two methods surfaceCreated and surfaceDestroyed inform the implementor that
the drawing surface is available for drawing and that it has become
unavailable, respectively. The argument to both of the calls is an
instance of yet a third class, SurfaceHolder. In the interval
between these two calls, a drawing routine can call the
SurfaceView methods lockCanvas and unlockCanvasAndPost to edit the pixels
there.
If this seems complex, even alongside some of the elaborate
animation discussed previously…well, it is. As usual, concurrency
increases the likelihood of nasty, hard-to-find bugs. The client of a
SurfaceView must be sure that access to any state
shared across threads is properly synchronized, and also that it never
touches the SurfaceView, Surface,
or
Canvas except in the
interval between the calls to surfaceCreated and surfaceDestroyed. The toolkit could
clearly benefit from a more complete framework support for
SurfaceView animation.
If you are considering SurfaceView animation, you are probably also
considering OpenGL graphics. As we’ll see, there is an extension
available for OpenGL animation on a SurfaceView. It will turn up in a somewhat
out-of-the-way place, though.