使用计时器(或处理程序)通过更改ImageView中的图像创建动画

5
我希望通过更改 ImageView 中的帧来创建一个简单的动画。我不想使用 AnimationDrawable,因为我需要在帧更改或停止动画时接收事件,以便能够倒放、重新启动等等。
我的问题是,即使在主线程上调用 setImageDrawable,帧也实际上没有改变。所以一切似乎都很正常,除了帧没有改变(实际上只有一个帧被绘制,即第一个帧)。
因此,我的代码如下:
public class AnimatedImageView extends ImageView implements Animatable, Runnable {

    private static final String TAG = "AnimatedImageView";

    public static interface AnimationEventsListener {
        void animationDidChangeFrame(AnimatedImageView animatedImageView);

        void animationDidStop(AnimatedImageView animatedImageView);
    }

    /* frames - ress ids */
    private int[] mFrameResIds;
    /* current frame index */
    private int mCurrentFrameIdx;
    /* number of the current repeat */
    private int loopIndex;
    /* number of animation repetiotions, 0 for infinite */
    private int mNumOfRepetitions;
    /* if animation is playing */
    private boolean playing;
    /* if animation is paused */
    private boolean paused;
    /* if animation is playing backward */
    private boolean playItBackward;
    /* animation duration */
    private long mAnimationDuration;
    /* frame animation duration (mAnimationDuration / num_of_frames) */
    private long mFrameAnimationDuration;

    /* listener for animation events */
    private AnimationEventsListener mListener;

    private Handler mHandler;

    public AnimatedImageView(Context context) {
        super(context);
        setup();
    }

    public AnimatedImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public AnimatedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup() {
        mHandler = new Handler(Looper.getMainLooper());
        this.setClickable(true);
        Logger.d("MSG", "setup, thread: " + Thread.currentThread().toString());
    }


    @Override
    public void start() {
        if (playing) {
            return;
        }
        mCurrentFrameIdx = playItBackward ? mFrameResIds.length - 1 : 0;
        loopIndex = 0;
        playing = true;
        updateFrame();
    }

    @Override
    public void stop() {
        paused = true;
    }

    public void resume() {
        paused = false;
        playing = true;
        updateFrame();
    }

    @Override
    public boolean isRunning() {
        return false;
    }

    @Override
    public void run() {
        Logger.d("MSG", "run, thread: " + Thread.currentThread().toString());
        updateFrame();
    }

    public AnimationEventsListener getListener() {
        return mListener;
    }

    public void setListener(AnimationEventsListener mListener) {
        this.mListener = mListener;
    }

    public long getAnimationDuration() {
        return mAnimationDuration;
    }

    public void setAnimationDuration(long mAnimationDuration) {
        this.mAnimationDuration = mAnimationDuration;
        if (mFrameResIds != null) {
            mFrameAnimationDuration = mAnimationDuration / mFrameResIds.length;
        }
    }

    public int[] getFrameResIds() {
        return mFrameResIds;
    }

    public void setFrameResIds(int[] mFrameResIds) {
        this.mFrameResIds = mFrameResIds;
        if (mFrameResIds != null) {
            mFrameAnimationDuration = mAnimationDuration / mFrameResIds.length;
        }
    }

    private void setCurrentFrame(int frameValue) {
        mCurrentFrameIdx = frameValue;
        this.setAnimationFrame(frameValue);
    }

    private void setAnimationFrame(int animationFrame) {
        Logger.d("MSG", "setAnimationFrame: " + animationFrame);
        if (animationFrame < 0) {
            animationFrame = 0;
        }

        if (animationFrame > -1) {
            animationFrame = mFrameResIds.length - 1;
        }

        Resources resources = getResources();
        AnimatedImageView.this.setImageDrawable(resources.getDrawable(mFrameResIds[animationFrame]));
        AnimatedImageView.this.forceLayout();
//        AnimatedImageView.this.setImageBitmap(BitmapFactory.decodeResource(resources, mFrameResIds[animationFrame]));
//        this.setImageResource(mFrameResIds[animationFrame]);
        AnimatedImageView.this.invalidate();
    }

    private void updateFrame() {
        Logger.d("MSG", "updateFrame " + mCurrentFrameIdx);
        if (!playing || paused) {
            return;
        }

        playing = false;

        if (mListener != null) {
            mListener.animationDidChangeFrame(AnimatedImageView.this);
        }

        if (!playItBackward) {
            mCurrentFrameIdx++;
            if (mCurrentFrameIdx >= mFrameResIds.length) {
                loopIndex++;
                mCurrentFrameIdx = 0;
                if (mNumOfRepetitions == loopIndex) {
                    if (mListener != null) {
                        mListener.animationDidStop(AnimatedImageView.this);
                    }
                    return;
                }
            }
        } else {
            mCurrentFrameIdx--;
            if (mCurrentFrameIdx < 0) {
                loopIndex++;
                mCurrentFrameIdx = mFrameResIds.length - 1;
                if (mNumOfRepetitions == loopIndex) {
                    if (mListener != null) {
                        mListener.animationDidStop(AnimatedImageView.this);
                    }
                    return;
                }
            }
        }

        playing = true;

        setAnimationFrame(mCurrentFrameIdx);
//        mHandler.postDelayed(AnimatedImageView.this, mFrameAnimationDuration);
//        scheduleDrawable(getResources().getDrawable(mFrameResIds[mCurrentFrameIdx]), AnimatedImageView.this, mFrameAnimationDuration);
//        postDelayed()
        postDelayed(AnimatedImageView.this, mFrameAnimationDuration);

//        SongPlayerActivity.handler.postDelayed(AnimatedImageView.this, mFrameAnimationDuration);
    }

我在xml布局中定义了一个AnimatedImageView对象,在activity中通过id找到它并在点击事件中启动动画:

final AnimatedImageView mDriverAnimation = (AnimatedImageView) findViewById(R.id.driver);
        mDriverAnimation.setFrameResIds(new int[]{R.drawable.song1_driver1, R.drawable.song1_driver2, R.drawable.song1_driver3});
        mDriverAnimation.setAnimationDuration(3 * 1000);
        mDriverAnimation.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mDriverAnimation.start();
            }
        });

有任何想法吗? :) 谢谢。
1个回答

10

不是这样的,我想手动更改帧,以便可以调用我的监听器。如果我使用TransitionDrawable,仍然无法在每个帧之后收到通知。谢谢你的回答。 - laurentiugh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接