Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android AnimationDrawable and knowing when animation ends

Tags:

android

I want to do an animation with several image-files, and for this the AnimationDrawable works very well. However, I need to know when the animation starts and when it ends (i.e add a listener like the Animation.AnimationListener). After having searched for answers, I'm having a bad feeling the AnimationDrawable does not support listeners..

Does anyone know how to create a frame-by-frame image animation with a listener on Android?

like image 340
LostDroid Avatar asked Feb 06 '10 21:02

LostDroid


People also ask

How do I turn off infinite animation on Android?

Use clearAnimation() to stop an animation.

What are the two different types of view animations in Android?

The animations are basically of three types as follows: Property Animation. View Animation. Drawable Animation.

Is animation possible on Android?

On Android 4.4 (API level 19) and higher, you can use the transition framework to create animations when you swap the layout within the current activity or fragment. All you need to do is specify the starting and ending layout, and what type of animation you want to use.


2 Answers

After doing some reading, I came up with this solution. I'm still surprised there isn't a listener as part of the AnimationDrawable object, but I didn't want to pass callbacks back and forward so instead I created an abstract class which raises an onAnimationFinish() method. I hope this helps someone.

The custom animation drawable class:

public abstract class CustomAnimationDrawableNew extends AnimationDrawable {      /** Handles the animation callback. */     Handler mAnimationHandler;      public CustomAnimationDrawableNew(AnimationDrawable aniDrawable) {         /* Add each frame to our animation drawable */         for (int i = 0; i < aniDrawable.getNumberOfFrames(); i++) {             this.addFrame(aniDrawable.getFrame(i), aniDrawable.getDuration(i));         }     }      @Override     public void start() {         super.start();         /*          * Call super.start() to call the base class start animation method.          * Then add a handler to call onAnimationFinish() when the total          * duration for the animation has passed          */         mAnimationHandler = new Handler();         mAnimationHandler.post(new Runnable() {             @Override             public void run() {                 onAnimationStart();             }           };         mAnimationHandler.postDelayed(new Runnable() {             @Override             public void run() {                 onAnimationFinish();             }         }, getTotalDuration());      }      /**      * Gets the total duration of all frames.      *       * @return The total duration.      */     public int getTotalDuration() {          int iDuration = 0;          for (int i = 0; i < this.getNumberOfFrames(); i++) {             iDuration += this.getDuration(i);         }          return iDuration;     }      /**      * Called when the animation finishes.      */     public abstract void onAnimationFinish();    /**      * Called when the animation starts.      */     public abstract void onAnimationStart(); } 

To use this class:

    ImageView iv = (ImageView) findViewById(R.id.iv_testing_testani);      iv.setOnClickListener(new OnClickListener() {         public void onClick(final View v) {              // Pass our animation drawable to our custom drawable class             CustomAnimationDrawableNew cad = new CustomAnimationDrawableNew(                     (AnimationDrawable) getResources().getDrawable(                             R.drawable.anim_test)) {                 @Override                 void onAnimationStart() {                     // Animation has started...                 }                  @Override                 void onAnimationFinish() {                     // Animation has finished...                 }             };              // Set the views drawable to our custom drawable             v.setBackgroundDrawable(cad);              // Start the animation             cad.start();         }     }); 
like image 189
Ricky Avatar answered Sep 21 '22 01:09

Ricky


I needed to know when my one-shot AnimationDrawable completes, without having to subclass AnimationDrawable since I must set the animation-list in XML. I wrote this class and tested it on Gingerbread and ICS. It can easily be extended to give a callback on each frame.

/**  * Provides a callback when a non-looping {@link AnimationDrawable} completes its animation sequence. More precisely,  * {@link #onAnimationComplete()} is triggered when {@link View#invalidateDrawable(Drawable)} has been called on the  * last frame.  *   * @author Benedict Lau  */ public abstract class AnimationDrawableCallback implements Callback {      /**      * The last frame of {@link Drawable} in the {@link AnimationDrawable}.      */     private Drawable mLastFrame;      /**      * The client's {@link Callback} implementation. All calls are proxied to this wrapped {@link Callback}      * implementation after intercepting the events we need.      */     private Callback mWrappedCallback;      /**      * Flag to ensure that {@link #onAnimationComplete()} is called only once, since      * {@link #invalidateDrawable(Drawable)} may be called multiple times.      */     private boolean mIsCallbackTriggered = false;      /**      *       * @param animationDrawable      *            the {@link AnimationDrawable}.      * @param callback      *            the client's {@link Callback} implementation. This is usually the {@link View} the has the      *            {@link AnimationDrawable} as background.      */     public AnimationDrawableCallback(AnimationDrawable animationDrawable, Callback callback) {         mLastFrame = animationDrawable.getFrame(animationDrawable.getNumberOfFrames() - 1);         mWrappedCallback = callback;     }      @Override     public void invalidateDrawable(Drawable who) {         if (mWrappedCallback != null) {             mWrappedCallback.invalidateDrawable(who);         }          if (!mIsCallbackTriggered && mLastFrame != null && mLastFrame.equals(who.getCurrent())) {             mIsCallbackTriggered = true;             onAnimationComplete();         }     }      @Override     public void scheduleDrawable(Drawable who, Runnable what, long when) {         if (mWrappedCallback != null) {             mWrappedCallback.scheduleDrawable(who, what, when);         }     }      @Override     public void unscheduleDrawable(Drawable who, Runnable what) {         if (mWrappedCallback != null) {             mWrappedCallback.unscheduleDrawable(who, what);         }     }      //     // Public methods.     //      /**      * Callback triggered when {@link View#invalidateDrawable(Drawable)} has been called on the last frame, which marks      * the end of a non-looping animation sequence.      */     public abstract void onAnimationComplete(); } 

Here is how to use it.

AnimationDrawable countdownAnimation = (AnimationDrawable) mStartButton.getBackground(); countdownAnimation.setCallback(new AnimationDrawableCallback(countdownAnimation, mStartButton) {     @Override     public void onAnimationComplete() {         // TODO Do something.     } }); countdownAnimation.start(); 
like image 31
benhylau Avatar answered Sep 24 '22 01:09

benhylau