Is there a way to listen for animation end in Anim

2020-02-26 04:41发布

问题:

I have created an AnimatedVectorDrawable, it works pretty well, now I am looking for a way to change the animation or hide the view after it finishes. I was hoping there was a listener but it doesn't look like there is. Can someone help?

EDIT

So I found a workaround, but not a very elegant way. What I did was create a thread and poll if the animation is running.

new Runnable() {
    public void run() {
        while(mLoop) {
            if(mAnimatedVectorDrawable.isRunning()) {
                Thread.sleep(mPollingInterval);
            } else {
                mLoop = false;
                // TODO what ever
            }
        }
    }
};

If somebody finds a better solution, please share.

回答1:

My first instinct was to take the source code, add some callbacks, and create a custom drawable out of it. Of course, that would have meant no xml support.

It turns out that AnimatedVectorDrawable uses VectorDrawable's private method(s). So, this approach won't work.

We could create a simple wrapper class around AnimatedVectorDrawable and add callbacks:

public class AVDWrapper {

    private Handler mHandler;
    private Animatable mDrawable;
    private Callback mCallback;
    private Runnable mAnimationDoneRunnable = new Runnable() {

        @Override
        public void run() {
            if (mCallback != null)
                mCallback.onAnimationDone();
        }
    };

    public interface Callback {
        public void onAnimationDone();
        public void onAnimationStopped();
    }

    public AVDWrapper(Animatable drawable, 
                            Handler handler, Callback callback) {
        mDrawable = drawable;
        mHandler = handler;
        mCallback = callback;
    }

    // Duration of the animation
    public void start(long duration) {
        mDrawable.start();
        mHandler.postDelayed(mAnimationDoneRunnable, duration);
    }

    public void stop() {
        mDrawable.stop();
        mHandler.removeCallbacks(mAnimationDoneRunnable);

        if (mCallback != null)
            mCallback.onAnimationStopped();
    }
}

Your code would look like:

final Drawable drawable = circle.getDrawable();
final Animatable animatable = (Animatable) drawable;

AVDWrapper.Callback callback = new AVDWrapper.Callback() {
        @Override
        public void onAnimationDone() {
            tick.setAlpha(1f);
        }

        @Override
        public void onAnimationStopped() {
          // Okay
        }
    };

AVDWrapper avdw = new AVDWrapper(animatable, mHandler, callback);
//animatable.start();
avdw.start(2000L);

tick.setAlpha(0f);
//tick.animate().alpha(1f).setStartDelay(2000).setDuration(1).start();

// One wrapper is sufficient if the duration is same
final Drawable drawable2 = tick.getDrawable();
final Animatable animatable2 = (Animatable) drawable2;
animatable2.start();

But, this is exactly what you are doing with setStartDelay. So I don't know how useful this will be.

Edit: All this can also be implemented inside an extended AnimatedVectorDrawable. But, you'll lose xml support altogether.



回答2:

Not tried this yet but Android 6.0 has a registerAnimationCallback method from the Animatable2 interface for this

Edit: yep this works in Android 6.0 emulator:

mAnimatedVectorDrawable.registerAnimationCallback (new Animatable2.AnimationCallback(){
    public void onAnimationEnd(Drawable drawable){
        //Do something
    }
}

Edit2: looks like they haven't added support for this in the AnimatedVectorDrawableCompat from support lib 23.2+ :(

Edit3: it looks like this got added in support lib 25.3.0

Thanks Carson!



回答3:

It is strange that there is no direct API for getting this. I suppose a workaround that doesn't involve overrides would just be simply to find the animation in your set of animations that takes the most amount of time, then post a delayed runnable to hide the view.

int longestAnimationTime = 500; //miliseconds, defined in XML possibly?
drawable.start();
drawableView.postDelayed(new Runnable() {

        @Override
        public void run() {
            drawableView.setVisibility(View.GONE);
        }
}, longestAnimationTime);

Downside is that it involves hard coding your longest animation time, but as long as you are referencing the same constant value both in the animation XML and in code, it would work correctly, no overrides needed.



回答4:

I found the same problem. I am trying to animate a circle being drawn and a tick inside.

The best I could do is play with durations. Something like this:

final Drawable drawable = circle.getDrawable();
final Animatable animatable = (Animatable) drawable;
animatable.start();

tick.setAlpha(0f);
tick.animate().alpha(1f).setStartDelay(2000).setDuration(1).start();
final Drawable drawable2 = tick.getDrawable();
final Animatable animatable2 = (Animatable) drawable2;
animatable2.start();

It would be great to have a listener