«

»

Oct 17

Android frame animation revisited [w/ code]

So, been working hard on my projects, and discovered some interesting things in Android possibilities for frame animation. Last time I was using an HTML approach, because of memory consumption issues with using ImageViews. However now my approach is using View.onDraw(Canvas) to draw BMPs straight off files, in an asynchronous way, and it seems to work pretty good.
Let me tell you how I did it

The general idea was explained in the preface, and the technicality is as follows. I plainly subclass View, and override onDraw(Canvas):

public class MyCanvasView extends View {
...
private Bitmap bmp;
private Lock bmpLock;
private Animator a;
private float scale = 1.0f;
private Rect clip;

@Override
protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   ...
   clip = canvas.getClipBounds();
   w = clip.width();
   w2 = w/2.0f;
   h = clip.height();
   h2 = h/2.0f;

   bmpLock.lock();
   float left = scale*(w2 - (float)bmp.getWidth()/2.0f);
   float top = scale*(h2 - (float)bmp.getHeight()/2.0f);

   Bitmap _bmp = bmp;
   canvas.drawBitmap(_bmp, left, top, paint);
   bmpLock.unlock();
   ...
}
}

To drive the animation I privately subclass a Thread, that will push out new Bitmap objects (or at least write into the data of the private Bitmap object).

private class Animator extends Thread {
   private final MyCanvasView myCanvasView;
   private AssetManager assets;
   private int start = 0;
   private int end = 10;
   private boolean loop = false;

   public Animator(MyCanvasView myCanvasView) 
   {
      this.myCanvasView = myCanvasView;
      assets = myCanvasView.getContext().getAssets();
   }
		
   @Override
   public void run() {
   super.run();

   try {
      do {
         for (int i = start; i <= end; i++) {
            boolean bitmapLoaded;
            bitmapLoaded = tryLoadBitmap("anim" + new DecimalFormat("0000").format(i) + ".png");
            if(!bitmapLoaded) { break; }
         }
      } while(loop);
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
   }

   private boolean tryLoadBitmap(String bmpFilename) throws InterruptedException {
      try {
         bmpLock.lock();
         bmp = BitmapFactory.decodeStream(assets.open(bmpFilename));
         bmpLock.unlock();
         if(bmp == null) {
            AlertDialog a = new AlertDialog.Builder(getContext()).create();
            a.setMessage("Cannot load image");
            a.show();
            return false;
         }
         myCanvasView.postInvalidate();				
         sleep(25);
      } catch (IOException e) {
         e.printStackTrace();
      }
      return true;
   }
}

Notice the usage of the Lock bmpLock object to prevent racing conditions on the drawing and loading of new Bitmaps.
Also, you should probably want a mechanism to determine start, end and loop parameters. In the complete source you can see what I have done to address that.

Finally, a way to fire an animation on MyCanvasView:

public void fireAnimation() {
   if(a != null && a.isAlive()) { 
      a.interrupt(); 
      a = null;
   }
   a = new Animator(this);
   a.start(); //TODO: reuse the object
}

This runs in excellent frame rates, and it's not even fully optimized. So I guess the approach is correct for the Android environment. I did not, however, test this with many animators on the same layout.

Grab the complete source (including some stuff I didn't discuss here like rotating and scaling the "Sprite"):

https://raw.github.com/royshil/HeadFollower/master/src/edu/mit/media/fluid/royshil/graphics/MyCanvasView.java

This is part of the PoCoMo project I have been working on in our lab.

Enjoy!
Roy.

Share
  • Ashley

    Hi Roy,

    I require some help with iphone application i have refer to your previous post on augmented reality . can u contact me through my email @ manchesterlim@hotmail.com.

    Really interested in iphone programming and need help :(:(