package com.marshalchen.common.uimodule.switchbutton; import android.os.Handler; import android.os.Message; /** * controller of view animation * * @author kyleduo * @since 2014-09-25 * */ class AnimationController { private static int ANI_WHAT = 0x100; private static int DEFAULT_VELOCITY = 7; private static int DEFAULT_FRAME_DURATION = 1000 / 60; private AnimationHandler mHandler; private OnAnimateListener mOnAnimateListener; private boolean isAnimating = false; private int mFrame, mFrom, mTo; private int mVelocity = DEFAULT_VELOCITY; private AnimationController() { mHandler = new AnimationHandler(); } /** * get default AnimationController * @return */ static AnimationController getDefault() { AnimationController ac = new AnimationController(); return ac; } /** * initial an AnimationController with a listener * @param onAnimateListener NOT NULL * @return */ AnimationController init(OnAnimateListener onAnimateListener) { if (onAnimateListener == null) { throw new IllegalArgumentException("onAnimateListener can not be null"); } this.mOnAnimateListener = onAnimateListener; return this; } private static class AnimationHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == ANI_WHAT) { if (msg.obj != null) { ((Runnable) msg.obj).run(); } } } } void startAnimation(int from, int to) { this.isAnimating = true; this.mFrom = from; this.mTo = to; this.mFrame = mVelocity; if (mTo > mFrom) { this.mFrame = Math.abs(this.mVelocity); } else if (mTo < mFrom) { this.mFrame = -Math.abs(this.mVelocity); } else { this.isAnimating = false; this.mOnAnimateListener.onAnimateComplete(); return; } this.mOnAnimateListener.onAnimationStart(); new RequireNextFrame().run(); } void stopAnimation() { isAnimating = false; } /** * configure the velocity of animation * @param velocity a positive number */ public void setVelocity(int velocity) { if (velocity <= 0) { mVelocity = DEFAULT_VELOCITY; return; } mVelocity = velocity; } /** * calculate next frame in child thread * * @author kyleduo * */ class RequireNextFrame implements Runnable { @Override public void run() { if (!isAnimating) { return; } calcNextFrame(); mOnAnimateListener.onFrameUpdate(mFrame); if (mOnAnimateListener.continueAnimating()) { requireNextFrame(); } else { stopAnimation(); mOnAnimateListener.onAnimateComplete(); return; } } private void calcNextFrame() { // mFrame = mVelocity; } private void requireNextFrame() { Message msg = mHandler.obtainMessage(); msg.what = ANI_WHAT; msg.obj = this; mHandler.sendMessageDelayed(msg, DEFAULT_FRAME_DURATION); } } /** * interface for view animation * * @author kyle * */ interface OnAnimateListener { /** * invoked when the animation start */ public void onAnimationStart(); /** * ask view whether continue Animating * * @return boolean true for continueAnimating */ public boolean continueAnimating(); /** * a new frame is ready. * * @param frame next step of the animation, for linear animation, it is equal to velocity */ public void onFrameUpdate(int frame); /** * invoked when the animation complete */ public void onAnimateComplete(); } }