Android provides a convenient way to make frame animation via XML or programmatically. API is very clear and powerful. In the following example, we’re going to animate a character sprite programmatically via this API.

  1. Character sprite bitmap

First, we need to get a character sprite bitmap. Here, we choose the classic grossini dance :

grossini_dance

Each frame has the following dimensions : 85 x 121 pixels. The sprite bitmap has 14 frames : 5 in width and 3 in height.

 

  1. Cut each frame from the bitmap

To use the Android Animation API, we need to cut the main bitmap to get each frames. First, we put character sprite bitmap in assets folder of an Android project. In this folder, we’re sure that Android OS won’t try to resize this. The following loading is made like that :


private Bitmap getBitmapFromAssets(MainActivity mainActivity, String filepath) {
   AssetManager assetManager = mainActivity.getAssets();
   InputStream istr = null;
   Bitmap bitmap = null;

   try {
      istr = assetManager.open(filepath);
      bitmap = BitmapFactory.decodeStream(istr);
   } catch (IOException ioe) {
      // manage exception
   } finally {
      if (istr != null) {
         try {
            istr.close();
         } catch (IOException e) {
         }
      }
   }

   return bitmap;
}

Then, we need to create each frame inside a bitmaps array. Here, main idea is to use the static method Bitmap.createBitmap to create a new bitmap from the main bitmap given coordinates and dimensions. Code is like that :


// cut bitmaps from bird bmp to array of bitmaps
bmps = new Bitmap[NB_FRAMES];
int currentFrame = 0;

for (int i = 0; i < COUNT_Y; i++) {
   for (int j = 0; j < COUNT_X; j++) {
      bmps[currentFrame] = Bitmap.createBitmap(birdBmp, FRAME_W
         * j, FRAME_H * i, FRAME_W, FRAME_H);

      // apply scale factor
      bmps[currentFrame] = Bitmap.createScaledBitmap(
         bmps[currentFrame], FRAME_W * SCALE_FACTOR, FRAME_H
         * SCALE_FACTOR, true);

      if (++currentFrame >= NB_FRAMES) {
         break;
      }
   }
}

 

  1. Create frame Animation

With our array of bitmaps, we can now create a frame animation thanks to the AnimationDrawable class. Here, we want a repeated animation for the character so we must call the setOneShot method with false in parameter on the AnimationDrawable instance. Like we create the frames dynamically from a main bitmap, we use the Animation API programmatically and no inside an XML resources file. Code is here :


// create animation programmatically
final AnimationDrawable animation = new AnimationDrawable();
animation.setOneShot(false); // repeat animation

for (int i = 0; i < NB_FRAMES; i++) {
   animation.addFrame(new BitmapDrawable(getResources(), bmps[i]),
      FRAME_DURATION);
}

 

  1. Load frame Animation

Once frame Animation is created, we can load it into an ImageView instance. If you target a min SDK inferior to Version Code 16, you will need to make a statement to apply setBackgroundDrawable or setBackground on the ImageView instance like this :


// load animation on image
if (Build.VERSION.SDK_INT < 16) {
   img.setBackgroundDrawable(animation);
} else {
   img.setBackground(animation);
}

 

  1. Execute frame Animation

Last step but not the least, we need to execute frame Animation. To achieve that, we’re going to call the start method of the AnimationDrawable instance created previously. This call must be made in a Runnable that is applied on the post method of ImageView instance like that :


// start animation on image
img.post(new Runnable() {

   @Override
   public void run() {
      animation.start();
   }

});

 

  1. Complete code for the frame Animation

Here, you can find the complete code of the Activity for the frame Animation :


package com.ssaurel.animationsprite;

import java.io.IOException;
import java.io.InputStream;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;

public class MainActivity extends Activity {

 // frame width
 private static final int FRAME_W = 85;
 // frame height
 private static final int FRAME_H = 121;
 // number of frames
 private static final int NB_FRAMES = 14;
 // nb of frames in x
 private static final int COUNT_X = 5;
 // nb of frames in y
 private static final int COUNT_Y = 3;
 // frame duration
 // we can slow animation by changing frame duration
 private static final int FRAME_DURATION = 200; // in ms !
 // scale factor for each frame
 private static final int SCALE_FACTOR = 5;
 private ImageView img;
 // stores each frame
 private Bitmap[] bmps;

 @SuppressLint("NewApi") @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  img = (ImageView) findViewById(R.id.img);

  // load bitmap from assets
  Bitmap birdBmp = getBitmapFromAssets(this, "grossini_dance.png");

  if (birdBmp != null) {
    // cut bitmaps from bird bmp to array of bitmaps
    bmps = new Bitmap[NB_FRAMES];
    int currentFrame = 0;

    for (int i = 0; i < COUNT_Y; i++) {
     for (int j = 0; j < COUNT_X; j++) {
      bmps[currentFrame] = Bitmap.createBitmap(birdBmp, FRAME_W
       * j, FRAME_H * i, FRAME_W, FRAME_H);

      // apply scale factor
      bmps[currentFrame] = Bitmap.createScaledBitmap(
        bmps[currentFrame], FRAME_W * SCALE_FACTOR, FRAME_H
        * SCALE_FACTOR, true);

      if (++currentFrame >= NB_FRAMES) {
       break;
      }
    }
  }

  // create animation programmatically
  final AnimationDrawable animation = new AnimationDrawable();
  animation.setOneShot(false); // repeat animation

  for (int i = 0; i < NB_FRAMES; i++) {
    animation.addFrame(new BitmapDrawable(getResources(), bmps[i]),
      FRAME_DURATION);
  }

  // load animation on image
  if (Build.VERSION.SDK_INT < 16) {
    img.setBackgroundDrawable(animation);
  } else {
    img.setBackground(animation);
  }

  // start animation on image
  img.post(new Runnable() {

    @Override
    public void run() {
      animation.start();
    }

  });

 }
}

 private Bitmap getBitmapFromAssets(MainActivity mainActivity,
  String filepath) {
  AssetManager assetManager = mainActivity.getAssets();
  InputStream istr = null;
  Bitmap bitmap = null;

   try {
    istr = assetManager.open(filepath);
    bitmap = BitmapFactory.decodeStream(istr);
   } catch (IOException ioe) {
    // manage exception
   } finally {
    if (istr != null) {
      try {
        istr.close();
      } catch (IOException e) {
      }
    }
  }

  return bitmap;
 }

}

 

  1. Extra – Live coding of the tutorial in video on Youtube

As an extra, you can see the live coding of this tutorial in video on Youtube here :