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:
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);
}
};In
OnCreate
bind the pause and play buttons, fire the timer and set the firstImageView
@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
Suppress/Block Broadcastreceiver in Another App
How to Use the Gradle Build System for Android with Eclipse
Get File Path of Image on Android
Unzip a Zipped File on Sd Card in Android Application
Gridlayout and Row/Column Span Woe
Selected Tab's Color in Bottom Navigation View
Bluetooth Pairing Without User Confirmation
Startlescan with 128 Bit Uuids Doesn't Work on Native Android Ble Implementation
Displaying Images from a Specific Folder on the Sdcard Using a Gridview
Java.Lang.Noclassdeffounderror: Com.Google.Android.Gms.Internal.Zzmp
Android Detect Phone Lock Event
Gradle 7 and Jitpack.Io Runs into Error During Publish
Save Data and Change Orientation