How to Pause Frame Animation Using Animationdrawable

Pause AnimationDrawable and resume from the same frame

Since I needed a timer on the screen as well, the answer in my case was simply to create a runnable replace the ImageView background every second.

Here is the general idea:

  1. Create the runnable, ImageView and few helpers:

    ImageView exImage = null;

    long startTime = 0;
    long pauseTime = 0;
    long pauseStartTime = 0;

    List<Integer> animList = new ArrayList<>();
    int animIt = 0;

    Handler timerHandler = new Handler();
    Runnable timerRunnable = new Runnable() {
    @Override
    public void run() {

    long millis = System.currentTimeMillis() - startTime - pauseTime;
    double dSecs = (double) (millis / 100);
    int pace = 10;

    if (dSecs % pace == 0.0 && !animList.isEmpty()) {
    animIt = (animIt == animList.size() - 1) ? 0 : animIt + 1;
    exImage.setBackgroundResource(animList.get(animIt));
    }
    timerHandler.postDelayed(this, 100);
    }
    };
  2. In OnCreate bind the pause and play buttons, fire the timer and set the first ImageView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_exercise);

    // Set the image view
    exImage = (ImageView) findViewById(R.id.image_zero);
    exImage.setBackgroundResource(R.drawable.fe_0);

    // Start the timer
    timerHandler.postDelayed(timerRunnable, 0);

    // Bind the buttons
    ImageButton pp = (ImageButton) findViewById(R.id.button_play_pause_toggle);
    pp.setImageResource(R.drawable.pause);
    pp.setTag(R.drawable.pause);

    pp.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    ImageButton pp = (ImageButton) v;
    //Pause
    if ((Integer) pp.getTag() == R.drawable.pause) {
    pauseStartTime = System.currentTimeMillis();
    timerHandler.removeCallbacks(timerRunnable);
    } else { // Resume
    pauseTime += System.currentTimeMillis() - pauseStartTime;
    timerHandler.postDelayed(timerRunnable, 0);
    }
    }
    });
    }

AnimationDrawable Pause ?

I've been looking around, and it doesn't seem that there's a way to directly do it. All of the variables and methods for setting the frame in AnimationDrawable are private. However, you can get the index of the frame the animation is on when you hit pause and then create a new animation by iterating through the original animation from the index of the last frame played (and then start over and add the rest if the animation is cyclical).

I started out with an XML based animation loop, and then just added another one for pause and resume. Here's what I ended up doing, and it's working fine so far, with relevant variables listed first.

private ImageView sphere;
private AnimationDrawable sphereAnimation;
private AnimationDrawable sphereResume;
private AnimationDrawable activeAnimation;
private Drawable currentFrame;
private Drawable checkFrame;
private int frameIndex;

private void pause()
{
looping = false;
sphereResume = new AnimationDrawable();
activeAnimation.stop();
currentFrame = activeAnimation.getCurrent();

frameLoop:
for(int i = 0; i < sphereAnimation.getNumberOfFrames(); i++)
{
checkFrame = activeAnimation.getFrame(i);

if(checkFrame == currentFrame)
{
frameIndex = i;
for(int k = frameIndex; k < activeAnimation.getNumberOfFrames(); k++)
{
Drawable frame = activeAnimation.getFrame(k);
sphereResume.addFrame(frame, 50);
}
for(int k = 0; k < frameIndex; k++)
{
Drawable frame = activeAnimation.getFrame(k);
sphereResume.addFrame(frame, 50);
}
activeAnimation = sphereResume;
sphere.setImageDrawable(activeAnimation);
sphere.invalidate();
break frameLoop;
}
}
}

private void play()
{
looping = false;
activeAnimation.setOneShot(true);
activeAnimation.start();
}

private void stop()
{
looping = false;
activeAnimation.stop();
activeAnimation = sphereAnimation;
sphere.setImageDrawable(activeAnimation);
}

private void loop()
{
looping = true;
stopSoundEffect();
activeAnimation.setOneShot(false);
activeAnimation.start();
}

Android: Stop AnimationDrawable after specified time, resume onResume(), pause onPause()

Here is my code.

Implementation:

/**
* Adds some new features which are not available in the {@link AnimationDrawable} class.
* The main difference is that this class allows setting a timer, which indicates for how
* long is the animation supposed to be rendered.
*/
public class CustomAnimation
{
//==============================================================================================
private AnimationDrawable animation;

//==============================================================================================
private Drawable currentFrame;

//==============================================================================================
private Callable<Void> afterDelay;

//==============================================================================================
private long lastCheckedTime;
private long duration;

//==============================================================================================
private Runnable animationFinished;
private Handler handlerAnimation;

//==============================================================================================
private boolean infinite;

/**
* @param animation the {@link AnimationDrawable} which is about to animate
*/
public CustomAnimation(AnimationDrawable animation)
{
this.animation = animation;
}

/**
* Sets a time limit after which is the animation finished.
* @param duration time in milliseconds. Set to -1 if the animation is infinite.
*/
public void setDuration(long duration)
{
this.duration = duration;
if(duration == -1) infinite = true;
}

/**
* Starts the animation from the first frame, looping if necessary.
*/
public void animate()
{
animation.start();
lastCheckedTime = System.currentTimeMillis();
stopAfterDelay();
}

/**
* Cancels the animation.
*/
public void cancel()
{
animation.stop();
if(handlerAnimation != null) handlerAnimation.removeCallbacks(animationFinished);
}

/**
* Pauses the animation.
*/
public void pause()
{
currentFrame = animation.getCurrent();
duration = getRemainingTime();
animation.setVisible(false, false);
}

/**
* Pauses the animation.
* Call this method only in the onPause() callback.
*/
public void onPause()
{
currentFrame = animation.getCurrent();
duration = getRemainingTime();
cancel();
}

/**
* Resumes the animation.
*/
public void resume()
{
if(!animation.isRunning())
{
lastCheckedTime = System.currentTimeMillis();
animation.setVisible(true, false);
setFrame();
stopAfterDelay();
}
}

/**
* Resumes the animation.
* Call this method only in the onResume() callback.
*/
public void onResume()
{
if(!animation.isRunning())
{
lastCheckedTime = System.currentTimeMillis();
animation.start();
setFrame();
stopAfterDelay();
}
}

/**
* Sets the callable method which is called right after the animation finishes.
* @param afterDelay the callable method which returns nothing
*/
public void setCallableAfterDelay(Callable<Void> afterDelay)
{
this.afterDelay = afterDelay;
}

/**
* @return remaining time in milliseconds until the animation is supposed to be finished
*/
public long getRemainingTime()
{
return duration - (System.currentTimeMillis() - lastCheckedTime);
}

private void stopAfterDelay()
{
if(infinite) return;

animationFinished = new Runnable()
{
@Override
public void run()
{
animation.stop();

try { if(afterDelay != null) afterDelay.call(); }
catch(Exception e) { e.printStackTrace(); }
}
};
handlerAnimation = new Handler();
handlerAnimation.postDelayed(animationFinished, duration);
}

private void setFrame()
{
for(int i = 0; i < animation.getNumberOfFrames(); i++)
{
Drawable checkFrame = animation.getFrame(i);
if(checkFrame == currentFrame) break;
animation.run();
}
}
}

Usage:

Launch:

CustomAnimation animation = new CustomAnimation((AnimationDrawable) img.getBackground());
animation.setDuration(5000);
animation.setCallableAfterDelay(new Callable<Void>()
{
@Override
public Void call()
{
img.setBackgroundResource(R.drawable.imgFinished);
return null;
}
});

animation.animate();

onPause() callback:

@Override
public void onPause()
{
super.onPause();
animation.onPause();
}

onResume() callback:

@Override
public void onResume()
{
super.onResume();
animation.onResume();
}

how to stop frame animation when mediaplayer completes playback?

You should set a MediaPlayer.OnCompletionListener with setOnCompletionListener(): then you'll get a callback when the MediaPlayer finishes and you can stop your animation.



Related Topics



Leave a reply



Submit