Android provides several ways to implement Animations. But for different situation, we have to choose different approach. For example, if we want to create a menu dropdown animation or view slider effect, we can choose Android tweened animation. It is the basic Android animation engine which animated the view’s position, size, rotation. In this article Android View Animation Example, I have talked about this. Here is another case, if we want to create a loading icon animation or a character with regular animation, Android frame by frame animation mechanism is the best choice. You can find more information in this article Android Frame Animation Example in Flappy Bird.

However, all animation mechanisms mentioned above are still not enough to cover all scenarios. For example, how can I control the frame rate in frame by frame animation? We can start or stop the frame by frame animation, but how to pause the animation. To solve these problem, I suggest we can use sprite sheet animation. In Android, we can create a sprite sheet animations by drawing frames on Canvas within SurfaceView. In this tutorial, I will show you how to draw images on Canvas within Thread frame by frame. The frame rate is controlled by thread sleeping.

sprite sheet animation in Android

Download And Try Example App On Android Device

Update to version 3.5
You can install this android example app on your android device. Click following link to download the app or scan the QR code below to install this app directly on your android device.

Click here to download APK file
chart

Layout XML Source Code Example

In the layout xml, I will put one SurfaceView on the top of the screen, followed by a Seekbar, which is used to adjust the frame rate. Below the Seekbar, I will put three buttons to start the animation, puase the animation and stop the animation. Now, let’s check the source code of layout xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.jms.flappybirdanimation.GameFragment" >

    <com.jms.viewcanvasanimation.FrameAnimationView
        android:id="@+id/birdimage"
        android:layout_width="174dp"
        android:layout_height="134dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="74dp" />

    <SeekBar
        android:id="@+id/seekBar1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button2"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="46dp"
        android:max="59"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:progress="1" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/seekBar1"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="20dp"
        android:layout_marginLeft="20dp"
        android:text="Frame Rate: "
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="24dp"
        android:text="Start"
        android:width="80dp" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button1"
        android:layout_alignBottom="@+id/button1"
        android:layout_centerHorizontal="true"
        android:text="Pause"
        android:width="80dp" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button3"
        android:layout_alignBottom="@+id/button3"
        android:layout_alignParentRight="true"
        android:layout_marginRight="25dp"
        android:text="Stop"
        android:width="80dp" />

</RelativeLayout>

Drawing Frame Animation on Canvas Within SurfaceView

In the source code, FrameAnimationView is a subclass of SurfaceView, which will draw the sprite sheet animation on its own Canvas. I will use thread to control the frame rate. The concept is each thread running cycle is one frame. Between the frames, I will call Thread.sleep() to control the frame rate. For example, if you want to create an animation with 30fps (30 frames per second), you have to let the thread sleep for 33 milliseconds. Here is the source code example for controlling drawing on Canvas in Thread in Android.


/**
* Drawing frame images on the canvas
*/
private synchronized void drawFrame(Canvas canvas) {
currentFrame++;
if (currentFrame < frames.length) { canvas.drawColor(0, Mode.CLEAR); canvas.drawBitmap(frames[currentFrame], 0, 0, null); } else if(isLoop){ currentFrame = 0; canvas.drawColor(0, Mode.CLEAR); canvas.drawBitmap(frames[currentFrame], 0, 0, null); } else { drawThread.isWaiting = true; } } /** * Thread, control the drawing process * */ public class DrawThread extends Thread { private SurfaceHolder surfaceHolder; private boolean isRunning = true; private boolean isWaiting = false; public DrawThread(SurfaceHolder holder) { this.surfaceHolder = holder; } @Override public void run() { super.run(); Canvas canvas = null; while (isRunning) { synchronized (surfaceHolder) { //draw current frame canvas = surfaceHolder.lockCanvas(); drawFrame(canvas); surfaceHolder.unlockCanvasAndPost(canvas); } try { //frame rate control sleep(framerate); //pause the animation if (isWaiting) { synchronized (surfaceHolder) { surfaceHolder.wait(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } } [/java] To start the sprite sheet animation, I will start the thread by start() if the thread has never been running. If the thread is locked, I just call notify() to unlock the thread. [java] public void startAnimation() { if (drawThread != null && !isPlaying) { if (drawThread.isWaiting) { isPlaying = true; drawThread.isWaiting = false; synchronized (getHolder()) { getHolder().notify(); } } else { isPlaying = true; drawThread.start(); } } } [/java] To pause the sprite sheet animation, I will lock the thread by wait(); In my example, I am using SurfaceHolder.wait() to lock the drawing process. [java] public void pauseAnimation() { if (drawThread != null) { drawThread.isWaiting = true; } isPlaying = false; } [/java] To stop the sprite sheet animation, I also lock the thread. Furthermore, I redraw the first frame image on the Canvas to reset the animation. [java] public synchronized void stopAnimation() { if (drawThread != null) { drawThread.isWaiting = true; } currentFrame = -1; Canvas canvas = getHolder().lockCanvas(); drawFrame(canvas); getHolder().unlockCanvasAndPost(canvas); isPlaying = false; } [/java] In my example, I also add a new feature which can allow me to jump to a specific frame. It is also a great function which Android native frame-by-frame animation cannot provide. The concept behind that is stop the thread and redraw the specific frame image on the Canvas. Let's check this example source code: [java] public void gotoFrame(int frame) { if (!isPlaying && frames!= null && frame < frames.length) { currentFrame = frame - 1; Canvas canvas = getHolder().lockCanvas(); drawFrame(canvas); getHolder().unlockCanvasAndPost(canvas); } } [/java]

Get Full Source Code under $5.99


You will get the whole Android sprite sheet animation example source code at $5.99. With this source code, you can do everything you want:

  • Replace with your own characters;
  • Monetize the app with your own AdMob publish id;
  • Use the source code in your own project;
  • Publish app in your android develop account;
Previous PostNext Post

5 Comments

      1. The reason I break the spritesheet into piece is because the spritesheet image should be very big… it is not efficient to load a very big image into memory.

        1. Thank you James. One more question,
          Are you using the meta-data (plist, xml, json ) file which specifies the sprite information in the sprite-sheet to parse it and render individual frames for the animation?

          1. In my example, I am using naming convention to control the sprite speet. In real app, I will use json to store the sprite information.

Leave a Reply

Your email address will not be published. Required fields are marked *