Android AnimationDrawable并知道动画何时结束

Los*_*oid 26 android

我想用几个图像文件做一个动画,为此,AnimationDrawable工作得很好.但是,我需要知道动画何时开始以及何时结束(即添加一个类似于Animation.AnimationListener的侦听器).在搜索完答案后,我感觉不到AnimationDrawable不支持听众.

有没有人知道如何在Android上使用监听器创建逐帧图像动画?

Ric*_*cky 45

做了一些阅读后,我想出了这个解决方案.我仍然感到惊讶的是,没有一个侦听器作为AnimationDrawable对象的一部分,但我不想反复传递回调,所以我创建了一个引发onAnimationFinish()方法的抽象类.我希望这可以帮助别人.

自定义动画可绘制类:

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();
}
Run Code Online (Sandbox Code Playgroud)

要使用此类:

    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();
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 不好的是,这个解决方案打破了'oneshot ='true'. (2认同)

ben*_*lau 25

我需要知道我的一次性AnimationDrawable何时完成,而不必继承AnimationDrawable子类,因为我必须在XML中设置动画列表.我写了这个课,并在Gingerbread和ICS上进行了测试.可以轻松扩展它以在每个帧上进行回调.

/**
 * 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();
}
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它.

AnimationDrawable countdownAnimation = (AnimationDrawable) mStartButton.getBackground();
countdownAnimation.setCallback(new AnimationDrawableCallback(countdownAnimation, mStartButton) {
    @Override
    public void onAnimationComplete() {
        // TODO Do something.
    }
});
countdownAnimation.start();
Run Code Online (Sandbox Code Playgroud)

  • 只是为了澄清,它只在动画完成时通知最后一帧.如果您想在每帧播放动画时收听,请使用此更新版本:https://gist.github.com/benhylau/a7d21c8806df9c73da7d (3认同)

Rus*_*hyn 22

可以通过覆盖AnimationDrawable类中的selectDrawable方法轻松跟踪动画结束.完整代码如下:

public class AnimationDrawable2 extends AnimationDrawable
{
    public interface IAnimationFinishListener
    {
        void onAnimationFinished();
    }

    private boolean finished = false;
    private IAnimationFinishListener animationFinishListener;

    public IAnimationFinishListener getAnimationFinishListener()
    {
        return animationFinishListener;
    }

    public void setAnimationFinishListener(IAnimationFinishListener animationFinishListener)
    {
        this.animationFinishListener = animationFinishListener;
    }

    @Override
    public boolean selectDrawable(int idx)
    {
        boolean ret = super.selectDrawable(idx);

        if ((idx != 0) && (idx == getNumberOfFrames() - 1))
        {
            if (!finished)
            {
                finished = true;
                if (animationFinishListener != null) animationFinishListener.onAnimationFinished();
            }
        }

        return ret;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 绝对正确的答案,检查持续时间随着时间的推移,或在较弱的设备上 (2认同)

hit*_*h45 16

我使用了一个递归函数来检查当前帧是否是每次检查毫秒的最后一帧.

private void checkIfAnimationDone(AnimationDrawable anim){
    final AnimationDrawable a = anim;
    int timeBetweenChecks = 300;
    Handler h = new Handler();
    h.postDelayed(new Runnable(){
        public void run(){
            if (a.getCurrent() != a.getFrame(a.getNumberOfFrames() - 1)){
                checkIfAnimationDone(a);
            } else{
                Toast.makeText(getApplicationContext(), "ANIMATION DONE!", Toast.LENGTH_SHORT).show();
            }
        }
    }, timeBetweenChecks);
}
Run Code Online (Sandbox Code Playgroud)


lal*_*lei 1

我猜你的代码不起作用,因为你尝试从非 UI 线程修改视图。尝试从您的活动中调用 runOnUiThread(Runnable) 。我用它在菜单的动画完成后淡出菜单。这段代码对我有用:

Animation ani =  AnimationUtils.loadAnimation(YourActivityNameHere.this, R.anim.fadeout_animation);
menuView.startAnimation(ani);

// Use Timer to set visibility to GONE after the animation finishes.            
TimerTask timerTask = new TimerTask(){
    @Override
    public void run() {
        YourActivityNameHere.this.runOnUiThread(new Runnable(){
            @Override
            public void run() {
                menuView.setVisibility(View.GONE);
            }
        });}};
timer.schedule(timerTask, ani.getDuration());
Run Code Online (Sandbox Code Playgroud)