Android

[Android] ステートマシンでアニメーションの順次実行を制御する

More than 1 year has passed since last update.

あるViewに対してアニメーション①を実行し、終了したらアニメーション②を実行し、、ということを実現したいとします。ただリスナーでアニメーションの終了を検知しているとコードがネストしたり、繰り返したい場合にどうすればいいか困ったりします。

そういう場合は、enum でステートマシンを作ってアニメーションを実行するとコードがすっきりします。

例えば下記のコードは、あるViewに対してアニメーション AnimState1 AnimState2 AnimState3 の順次実行を繰り返し行うコードの例です。

SomeAnimState.java
enum SomeAnimState {
    Stop {
        @Override
        protected void start(View view) {
            changeState(view, AnimState1);
        }
    },
    AnimState1 {
        @Override
        protected void enter(View view) {
            view.setAlpha(0);
            view.animate().alpha(1.0F).setDuration(1000).setInterpolator(new LinearInterpolator()).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    changeState(view, AnimState2);
                }
            });
        }
    },
    AnimState2 {
        @Override
        protected void enter(View view) {
            view.animate().scaleX(2.0F).scaleY(2.0F).setDuration(1000).setInterpolator(new OvershootInterpolator()).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    changeState(view, AnimState3);
                }
            });
        }
    },
    AnimState3 {
        @Override
        protected void enter(View view) {
            view.animate().scaleX(1.0F).scaleY(1.0F).setDuration(1500).setInterpolator(new FastOutSlowInInterpolator()).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    changeState(view, AnimState1);
                }
            });
        }
    },
    ;

    private static SomeAnimState currentState = Stop;

    public static void startAnim(View view) {
        currentState.start(view);
    }

    public static void stopAnim(View view) {
        currentState.stop(view);
    }

    protected void start(View view) {}

    @CallSuper
    protected void stop(View view) {
        currentState.changeState(view, Stop);
    }

    protected void enter(View view) {} // 各ステートが開始した際にさせる処理をオーバーライドで記述

    protected void exit(View view) {} // 各ステートが終了する際にさせる処理をオーバーライドで記述

    protected void changeState(View view, SomeAnimState nextState) {
        if (currentState != this) return;
        currentState.exit(view);
        currentState = nextState;
        currentState.enter(view);
    }
}

アニメーションを開始する場合は、staticメソッド startAnim() を呼び出します。すると各ステートの start() を呼び出すようになっているので、もし Stop ステータスでない場合は、何もしないようになっています。

アニメーションの開始
SomeAnimState.startAnim(someView);

同様に、繰り返しアニメーションにしている場合等でアニメーションを終了する場合は、staticメソッド stopAnim() を呼び出します。

アニメーションの終了
SomeAnimState.stopAnim(someView);

繰り返しにしている/していないに関わらず、アニメーション実行中にActivityの終了等でViewが死ぬケースがあるので、onDestroy() できちんと終了を呼び出しておくと安全です。

以上