Learn to make a frame animation with a character sprite in Android
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.
- Character sprite bitmap
First, we need to get a character sprite bitmap. Here, we choose the classic 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.
- 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; } } }
- 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); }
- 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); }
- 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(); } });
- 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; } }
- 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 :
Leave a Reply
You must be logged in to post a comment.