1/12/2011

Android: Canvas Frame by Frame Animation Tutorial

In this tutorial I will outline how to play an animation on a canvas on Android.
As you may already know, the canvas is the standard way of drawing on Android.
Drawing to a canvas is fairly straightforward.  You typically extend a View class and override the onDraw method.
In this case we will override ImageView for the sake of simplicity.

I will be using Eclipse for this tutorial.
First, create a new Android project with Eclipse.  You can use 1.5 as the target.  I've named the application animationtest.
Also, make sure you create a default activity, I call mine 'DefaultActivity.'
The package name I'll be using is com.solrpg.animationtest.

After you have created your Android project, create a new class named MyAnimationView.  
This class will extend ImageView and we'll do our drawing here.

Now, your view is ready to use.  Because DefaultActivity simply sets the activity layout to R.layout.main we'll need to modify it.
So, open up res/layout/main.xml and remove the TextView entry and replace it with your custom view using this xml: http://github.com/therevoltingx/android_animation_test/blob/master/res/layout/main.xml

Now we're ready to display our animation.  I have prepared a set of png images from a gif.
You can see the original gif here: http://solrpg.com/resources/attacks/2/2.gif
Then I imported them into res/drawables/ naming them shark_0.png shark_1.png shark_2.png and so on.
Where each number is the frame, all together this animation is 16 frames.

Now it's time to go back to MyAnimationView and add our animation code.  Basically, android calls onDraw every time the view needs to be drawn.
Typically this only happens once when the activity view is being set up.
After that, we have to call invalidate() on the view to have Android call onDraw().

First, we have to load the resource drawables into an array of bitmaps which can be drawn on the canvas.
So, we add a loadAnimation() method to MyAnimationView.  This method simply loads the frames from the res/drawables/ folder into a list of bitmaps.
In the example, we load the bitmaps when the default activity is created.
Once all the bitmaps are loaded, the animation is ready to be drawn.

Now, back to our onDraw method.  
First we have two private state variables called mIsPlaying and mStartPlaying.
Every time onDraw is called we check wether either one of these is set and draw the bitmap frame accordingly.
In order to start an animation we simply set mStartPlaying to true and call invalide() to have Android call onDraw().

When an animation is playing the variable mIsPlaying is set to true it checks which frame it has to draw.
In order to know which frame to play we need two variables play_frame and last_tick.
play_frame is set to the frame index of the animation that we're about to draw.
last_tick is set to a system time stamp.  This time stamp is set every time a frame changes.
This lets us calculate how long the current frame has been drawing.  
Once a specified amount of time has passed it increases the play_frame counter and calls invalidate()

Now you're ready to draw the animation.  We add a method called playAnimation() which simply sets mStartPlaying to true, then it calls invalidate().
In the tutorial application I added a button that when pressed calls playAnimation().

That's it!  Drawing an animation this way is fairly easy, I hope someone finds this helpful.

23 comments:

  1. Excellent tutorial! But when i try running the emulator i get Force Close. Any ideas of what the problem could be?

    ReplyDelete
  2. I just tested it on the emulator running Android 2.2 and it runs just fine. Can you send me or post the stacktrace of the force close?

    Thanks

    ReplyDelete
    Replies
    1. i Also use Android 2.2 emulator. But when i try running the emulator i get Force Close here i attach my error :::::

      01-21 13:00:16.534: D/AndroidRuntime(282): Shutting down VM
      01-21 13:00:16.534: W/dalvikvm(282): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
      01-21 13:00:16.574: E/AndroidRuntime(282): FATAL EXCEPTION: main
      01-21 13:00:16.574: E/AndroidRuntime(282): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.animationtest/com.example.animationtest.DefaultActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class com.solrpg.animationtest.MyAnimationView

      Delete
  3. For some reason i dont have a stacktrace available but this is the error in the log after running the emulator:

    AndroidManifest: Ignoring unknown 'com.example.animationtest2.MyAnimationView' XML

    ReplyDelete
  4. Weird. And it compiles ok? Can you tell me what version of the android SDK you are using, and which version you are targeting?

    ReplyDelete
  5. sdk 7. Ive tried with 2.1 and 2.2

    ReplyDelete
  6. Nvm! got it to work thanx for the help

    ReplyDelete
  7. Hi Miguel

    Excellent tutorial, that actually works!
    Bearing in mind this is only the second tutorial I've got to work (the other being an another superb example at: http://savagelook.com/blog/android/swipes-or-flings-for-navigation-in-android ), can you further describe how one might go about doing multiple "AnimationView"s?
    As you can see from the other tutorial, my aim would be to 'swipe/fling' to other anim's on other 'views'.
    I have already successfully imploded your tutorial with the other and, presently, have one anim on multiple swipe views...not that clever really...
    I can upload work so far if you're interested.
    do please bear in mind that I'm a mere designer (nologo.co.uk) and v.old so please talk/code slowly.
    kindest
    pils

    ReplyDelete
  8. Hi Miguel!

    I am confused at a point that how and at what point I should recycle the BitMaps to prevent OutOfMemoryException. I've a large number of images (more than 100) and I want to make animation from them. Kindly, guide me to recycle the BitMaps in your class, at what point and how?

    Thanks,

    ReplyDelete
  9. Hassan,

    That's a good question, and honestly a lot would be depend on how you app behaves and how often you'll be playing a certain animation. If you plan to play it often, keep it in memory and release it when the app is about to finish.
    If not, simply release them when after you've played the animation.

    ReplyDelete
  10. I wish if you could have loaded some screenshots

    ReplyDelete
  11. Please ignore my coment I got it I was looking into github

    ReplyDelete
  12. Thanks Miguel, you're a lifesaver!

    ReplyDelete
  13. is it posible that i save the images on the sdcard and create a frame by frame animation from sd card?

    ReplyDelete
  14. Bryan,

    Yes, that is possible But the animation may not be smooth if you load them from the sd card as you play it. That is because reading from the sdcard is slow.

    ReplyDelete
  15. Hello buddy i am getting the error message ERROR/AndroidRuntime(627): java.lang.IndexOutOfBoundsException: Invalid index 16, size is 16. so the program is not running what to do...?

    ReplyDelete
  16. hi miguel i want to modify it .
    i want to make a game and load the 2 or 3rd image after the player moves like in games we saw the background changes so what changing i have to this code i am new to android
    thaks

    ReplyDelete
  17. San, download the source via git and build from there.
    Anonymous, you can go ahead and modify the source code to do whatever you want. I just posted this as an example.

    ReplyDelete
  18. Hello,
    This is a great tutorial and I have found some very useful information here. Thank you. unfortunately I am very new to this and have no xml or java experience. I have a question that I'm hoping you can answer. I am wanting to edit the android system/framework/framework-res.apk so that the unlock dials (ic_jog_dial_***) will rotate on the lock screen. I have some xml files similar to yours where the background of the notifications menu shows an animation while open. But I don't see where the xml references that location. Basically I just want to take what I have and edit it to draw the animation in a different location. In this case the ic_jog_dial_*** for the unlock and silent jog tab dials. I don't know how to upload the files like you have so I zipped everything and uploaded it here: http://www.mediafire.com/?0215x8bib4e9n61

    Please take a look at what I have and see if you can tell me what I need to do in order to make this work for the ic_jog_dial's.
    Thank you for your time.

    -Mike

    ReplyDelete
  19. Hello,
    I'm sorry but I simply don't have the time to provide individual support. You can try stackoverflow.com or the android porting mailing list.

    Good luck!

    ReplyDelete
  20. Just for the record, enabling "force 2D hardware acceleration" appears to break this sample.

    ReplyDelete
  21. hi i am trying to create my own animation. Does the String TAG = "AnimationTest:AnimationView"; needed to change accordingly?

    ReplyDelete
    Replies
    1. No, that's just for debugging/logging purposes.

      Delete