Android Animation

源码

/**
 * 可应用于View,Surfaces,Object的抽象动画类
 */
public abstract class Animation implements Cloneable {
    /**
     * 无限期重复动画
     */
    public static final int INFINITE = -1;

    /**
     * 当动画到达结尾并且重复计数设为INFINITE_REPEAT,或者到达某个固定值时,动画重新开始。
     */
    public static final int RESTART = 1;

    /**
     * 当动画到达结尾并且重复计数设为INFINITE_REPEAT,或者到达某个固定值时,动画倒序执行。 
     */
    public static final int REVERSE = 2;

    /**
     * 当getTransformation(long, Transformation)调用第一帧动画时,用来记录当前时间,在短动画中比较有用
     */
    public static final int START_ON_FIRST_FRAME = -1;

    /**
     * 以绝对像素量作为基准尺寸
     */
    public static final int ABSOLUTE = 0;

    /**
     * 指定浮动维度,相对自身变化,应该以一个float类型浮点数乘以自身的width和height
     */
    public static final int RELATIVE_TO_SELF = 1;

    /**
     * 相对于父窗口的变化,应该以float类型乘以父窗口的width和height
     */
    public static final int RELATIVE_TO_PARENT = 2;

    /**
     * 当前正在播放的动画保持Z轴顺序
     */
    public static final int ZORDER_NORMAL = 0;
    
    /**
     * 指定正在播放的动画位于其他动画上方(在屏幕最前方)
     */
    public static final int ZORDER_TOP = 1;
    
    /**
     * 指定正在播放的动画位于其他动画下方(在屏幕最后方)
     */
    public static final int ZORDER_BOTTOM = -1;

    private static final boolean USE_CLOSEGUARD
            = SystemProperties.getBoolean("log.closeguard.Animation", false);

    /**
     * Set by {@link #getTransformation(long, Transformation)} when the animation ends.
     */
    boolean mEnded = false;

    /**
     * Set by {@link #getTransformation(long, Transformation)} when the animation starts.
     */
    boolean mStarted = false;

    /**
     *当重复模式为REVERSE时设置该值
     */
    boolean mCycleFlip = false;

    /**
     * 这个值必须通过initialize函数设置为真,表明动画已经初始化完毕并且可以开始演示
     */
    boolean mInitialized = false;

    /**
     * 当动画播放完毕时是否回到动画执行前的状态,只有mFillEnable为真时才考虑该值的状态,其默认值为真
     */
    boolean mFillBefore = true;

    /**
     *动画执行完后保持在执行完毕状态
     */
    boolean mFillAfter = false;

    /**
     * 表面fillbefore是否应该被考虑
     */
    boolean mFillEnabled = false;    

    /**
     * 动画开始时间(毫秒级)
     */
    long mStartTime = -1;

    /**
     * 动画开始后的延迟时间(毫秒级),如果延迟时间>0,那么动画实际的开始时间是startTime+startOffset
     */
    long mStartOffset;

    /**
     * 动画播放时间(毫秒级)
     */
    long mDuration;

    /**
     * 动画重复次数,默认是一直重复
     */
    int mRepeatCount = 0;

    /**
     * 动画已经重复了多少次
     */
    int mRepeated = 0;

    /**
     * 重复模式 RESTART 或者 REVERSE
     */
    int mRepeatMode = RESTART;

    /**
     * Interpolartor接口,
     */
    Interpolator mInterpolator;

    /**
     * 监听动画的开始,结束,重复
     */
    AnimationListener mListener;

    /**
     * 设置Z轴方向的顺序模式
     */
    private int mZAdjustment;

    /**
     * 设置动画背景颜色
     */
    private int mBackgroundColor;

    /**
     * 缩放因子
     * value via getScaleFactor().
     */
    private float mScaleFactor = 1f;

    /**
     * Don't animate the wallpaper.
     */
    private boolean mDetachWallpaper = false;

    private boolean mMore = true;
    private boolean mOneMoreTime = true;

    RectF mPreviousRegion = new RectF();
    RectF mRegion = new RectF();
    Transformation mTransformation = new Transformation();
    Transformation mPreviousTransformation = new Transformation();

    private final CloseGuard guard = CloseGuard.get();

    private Handler mListenerHandler;
    private Runnable mOnStart;
    private Runnable mOnRepeat;
    private Runnable mOnEnd;

    /**
     * 声明一个新的动画,持续时间为0,默认的interpolator接口,fillbefore模式
     */
    public Animation() {
        ensureInterpolator();
    }

    /**
     * 用指定上下文和参数集创建一个动画
     * @param context the application environment
     * @param attrs the set of attributes holding the animation parameters
     */
    public Animation(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation);

        setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0));
        setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0));
        
        setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled));
        setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore));
        setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter));

        setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount));
        setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART));

        setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
        
        setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0));

        setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));

        final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);

        a.recycle();

        if (resID > 0) {
            setInterpolator(context, resID);
        }

        ensureInterpolator();
    }

    @Override
    protected Animation clone() throws CloneNotSupportedException {
        final Animation animation = (Animation) super.clone();
        animation.mPreviousRegion = new RectF();
        animation.mRegion = new RectF();
        animation.mTransformation = new Transformation();
        animation.mPreviousTransformation = new Transformation();
        return animation;
    }

    /**
     * 重置动画初始状态
     * @see #initialize(int, int, int, int)
     */
    public void reset() {
        mPreviousRegion.setEmpty();
        mPreviousTransformation.clear();
        mInitialized = false;
        mCycleFlip = false;
        mRepeated = 0;
        mMore = true;
        mOneMoreTime = true;
        mListenerHandler = null;
    }

    /**
     * 取消动画,如果设置了监听器,则监听会通知动画结束的动作。
     * 如果手动取消动画,那么下次开始动画之前必须调用reset方法 
     * @see #reset() 
     * @see #start() 
     * @see #startNow() 
     */
    public void cancel() {
        if (mStarted && !mEnded) {
            fireAnimationEnd();
            mEnded = true;
            guard.close();
        }
        // Make sure we move the animation to the end
        mStartTime = Long.MIN_VALUE;
        mMore = mOneMoreTime = false;
    }

    /**
     * @hide
     */
    public void detach() {
        if (mStarted && !mEnded) {
            mEnded = true;
            guard.close();
            fireAnimationEnd();
        }
    }

    /**
     * 动画是否初始化完成
     * @return Has this animation been initialized.
     * @see #initialize(int, int, int, int)
     */
    public boolean isInitialized() {
        return mInitialized;
    }

    /**
     * 初始化动画对象和其父对象的尺寸
     *当知道动画尺寸和其父控件尺寸时调用该方法,且需要在getTransformation之前调用
     *
     * @param width 动画对象的宽度
     * @param height 动画对象的高度
     * @param parentWidth 父控件的宽度
     * @param parentHeight 父控件的高度
     */
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        reset();
        mInitialized = true;
    }

    /**
     * 设置监听器句柄
     * @hide
     */
    public void setListenerHandler(Handler handler) {
        if (mListenerHandler == null) {
            mOnStart = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationStart(Animation.this);
                    }
                }
            };
            mOnRepeat = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationRepeat(Animation.this);
                    }
                }
            };
            mOnEnd = new Runnable() {
                public void run() {
                    if (mListener != null) {
                        mListener.onAnimationEnd(Animation.this);
                    }
                }
            };
        }
        mListenerHandler = handler;
    }

    /**
     *设置加速曲线,插值器从上下文和资源ID选取
     *
     * @param context The application environment
     * @param resID The resource identifier of the interpolator to load
     * @attr ref android.R.styleable#Animation_interpolator
     */
    public void setInterpolator(Context context, int resID) {
        setInterpolator(AnimationUtils.loadInterpolator(context, resID));
    }

    /**
     * 设置加速曲线,使用默认的线性插值器
     *
     * @param i The interpolator which defines the acceleration curve
     * @attr ref android.R.styleable#Animation_interpolator
     */
    public void setInterpolator(Interpolator i) {
        mInterpolator = i;
    }

    /**
     * 设置动画开始的偏移时间,用于多个动画在不同时间开始的复杂情况
     *
     * @param startOffset When this Animation should start, in milliseconds from
     *                    the start time of the root AnimationSet.
     * @attr ref android.R.styleable#Animation_startOffset
     */
    public void setStartOffset(long startOffset) {
        mStartOffset = startOffset;
    }

    /**
     * 设置动画持续时间,不能是负值。
     * 
     * @param durationMillis Duration in milliseconds
     *
     * @throws java.lang.IllegalArgumentException if the duration is < 0
     *
     * @attr ref android.R.styleable#Animation_duration
     */
    public void setDuration(long durationMillis) {
        if (durationMillis < 0) {
            throw new IllegalArgumentException("Animation duration cannot be negative");
        }
        mDuration = durationMillis;
    }

    /**
     * 调整运行时间,确保动画持续时间不会比设置的durationMills还长,同时也确保重复计数的运行时间也小鱼durationMills
     * 
     * @param durationMillis The maximum duration the animation is allowed
     * to run.
     */
    public void restrictDuration(long durationMillis) {
        // If we start after the duration, then we just won't run.
        if (mStartOffset > durationMillis) {
            mStartOffset = durationMillis;
            mDuration = 0;
            mRepeatCount = 0;
            return;
        }
        
        long dur = mDuration + mStartOffset;
        if (dur > durationMillis) {
            mDuration = durationMillis-mStartOffset;
            dur = durationMillis;
        }
        // If the duration is 0 or less, then we won't run.
        if (mDuration <= 0) {
            mDuration = 0;
            mRepeatCount = 0;
            return;
        }
        // Reduce the number of repeats to keep below the maximum duration.
        // The comparison between mRepeatCount and duration is to catch
        // overflows after multiplying them.
        if (mRepeatCount < 0 || mRepeatCount > durationMillis
                || (dur*mRepeatCount) > durationMillis) {
            // Figure out how many times to do the animation.  Subtract 1 since
            // repeat count is the number of times to repeat so 0 runs once.
            mRepeatCount = (int)(durationMillis/dur) - 1;
            if (mRepeatCount < 0) {
                mRepeatCount = 0;
            }
        }
    }
    
    /**
     * 按照一定比例缩放运行时间
     *
     * @param scale The amount to scale the duration.
     */
    public void scaleCurrentDuration(float scale) {
        mDuration = (long) (mDuration * scale);
        mStartOffset = (long) (mStartOffset * scale);
    }

    /**
     * 设置动画的开始时间,如果时间设置为START_ON_FIRST_FRAME,那么动画将在getTransformation调用时第一次启动
     *
     * @param startTimeMillis the start time in milliseconds
     */
    public void setStartTime(long startTimeMillis) {
        mStartTime = startTimeMillis;
        mStarted = mEnded = false;
        mCycleFlip = false;
        mRepeated = 0;
        mMore = true;
    }

    /**
     * 当动画开始时间是START_ON_FIRST_FRAME时的快捷启动方法
     * 
     */
    public void start() {
        setStartTime(-1);
    }

    /**
     * 在当前时间播放动画,播放的一种快捷使用方法
     */
    public void startNow() {
        setStartTime(AnimationUtils.currentAnimationTimeMillis());
    }

    /**
     * 设置重复模式 
     *
     * @param repeatMode {@link #RESTART} or {@link #REVERSE}
     * @attr ref android.R.styleable#Animation_repeatMode
     */
    public void setRepeatMode(int repeatMode) {
        mRepeatMode = repeatMode;
    }

    /**
     * 设置重复次数,0表示不重复.
     *
     * @param repeatCount the number of times the animation should be repeated
     * @attr ref android.R.styleable#Animation_repeatCount
     */
    public void setRepeatCount(int repeatCount) {
        if (repeatCount < 0) {
            repeatCount = INFINITE;
        }
        mRepeatCount = repeatCount;
    }

    /**
     * If fillEnabled is true, this animation will apply the value of fillBefore.
     *
     * @return true if the animation will take fillBefore into account
     * @attr ref android.R.styleable#Animation_fillEnabled
     */
    public boolean isFillEnabled() {
        return mFillEnabled;
    }

    /**
     * If fillEnabled is true, the animation will apply the value of fillBefore.
     * Otherwise, fillBefore is ignored and the animation
     * transformation is always applied until the animation ends.
     *
     * @param fillEnabled true if the animation should take the value of fillBefore into account
     * @attr ref android.R.styleable#Animation_fillEnabled
     *
     * @see #setFillBefore(boolean)
     * @see #setFillAfter(boolean)
     */
    public void setFillEnabled(boolean fillEnabled) {
        mFillEnabled = fillEnabled;
    }

    /**
     * If fillBefore is true, this animation will apply its transformation
     * before the start time of the animation. Defaults to true if
     * {@link #setFillEnabled(boolean)} is not set to true.
     * Note that this applies when using an {@link
     * android.view.animation.AnimationSet AnimationSet} to chain
     * animations. The transformation is not applied before the AnimationSet
     * itself starts.
     *
     * @param fillBefore true if the animation should apply its transformation before it starts
     * @attr ref android.R.styleable#Animation_fillBefore
     *
     * @see #setFillEnabled(boolean)
     */
    public void setFillBefore(boolean fillBefore) {
        mFillBefore = fillBefore;
    }

    /**
     * If fillAfter is true, the transformation that this animation performed
     * will persist when it is finished. Defaults to false if not set.
     * Note that this applies to individual animations and when using an {@link
     * android.view.animation.AnimationSet AnimationSet} to chain
     * animations.
     *
     * @param fillAfter true if the animation should apply its transformation after it ends
     * @attr ref android.R.styleable#Animation_fillAfter
     *
     * @see #setFillEnabled(boolean) 
     */
    public void setFillAfter(boolean fillAfter) {
        mFillAfter = fillAfter;
    }

    /**
     * 设置Z方向顺序
     * 
     * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL},
     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
     * @attr ref android.R.styleable#Animation_zAdjustment
     */
    public void setZAdjustment(int zAdjustment) {
        mZAdjustment = zAdjustment;
    }
    
    /**
     * 设置背景颜色
     *
     * @param bg The background color.  If 0, no background.  Currently must
     * be black, with any desired alpha level.
     */
    public void setBackgroundColor(int bg) {
        mBackgroundColor = bg;
    }

    /**
     * The scale factor is set by the call to <code>getTransformation</code>. Overrides of 
     * {@link #getTransformation(long, Transformation, float)} will get this value
     * directly. Overrides of {@link #applyTransformation(float, Transformation)} can
     * call this method to get the value.
     * 
     * @return float The scale factor that should be applied to pre-scaled values in
     * an Animation such as the pivot points in {@link ScaleAnimation} and {@link RotateAnimation}.
     */
    protected float getScaleFactor() {
        return mScaleFactor;
    }

    /**
     * 是否带墙纸播放
     *
     * @param detachWallpaper true if the wallpaper should be detached from the animation
     * @attr ref android.R.styleable#Animation_detachWallpaper
     */
    public void setDetachWallpaper(boolean detachWallpaper) {
        mDetachWallpaper = detachWallpaper;
    }

    /**
     * Gets the acceleration curve type for this animation.
     *
     * @return the {@link Interpolator} associated to this animation
     * @attr ref android.R.styleable#Animation_interpolator
     */
    public Interpolator getInterpolator() {
        return mInterpolator;
    }

    /**
     * 获取动画开始播放的时间,如果动画还没有开始播放,返回START_ON_FIRST_FRAME
     *
     * @return the time in milliseconds when the animation should start or
     *         {@link #START_ON_FIRST_FRAME}
     */
    public long getStartTime() {
        return mStartTime;
    }

    /**
     * 获取动画播放的持续时间
     *
     * @return the duration in milliseconds of the animation
     * @attr ref android.R.styleable#Animation_duration
     */
    public long getDuration() {
        return mDuration;
    }

    /**
     * 获取偏移时间
     *
     * @return the start offset in milliseconds
     * @attr ref android.R.styleable#Animation_startOffset
     */
    public long getStartOffset() {
        return mStartOffset;
    }

    /**
     * 获取重复模式
     *
     * @return either one of {@link #REVERSE} or {@link #RESTART}
     * @attr ref android.R.styleable#Animation_repeatMode
     */
    public int getRepeatMode() {
        return mRepeatMode;
    }

    /**
     * 获取重复次数,默认为0
     *
     * @return the number of times the animation should repeat, or {@link #INFINITE}
     * @attr ref android.R.styleable#Animation_repeatCount
     */
    public int getRepeatCount() {
        return mRepeatCount;
    }

    /**
     * If fillBefore is true, this animation will apply its transformation
     * before the start time of the animation. If fillBefore is false and
     * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until
     * the start time of the animation.
     *
     * @return true if the animation applies its transformation before it starts
     * @attr ref android.R.styleable#Animation_fillBefore
     */
    public boolean getFillBefore() {
        return mFillBefore;
    }

    /**
     * If fillAfter is true, this animation will apply its transformation
     * after the end time of the animation.
     *
     * @return true if the animation applies its transformation after it ends
     * @attr ref android.R.styleable#Animation_fillAfter
     */
    public boolean getFillAfter() {
        return mFillAfter;
    }

    /**
     * Returns the Z ordering mode to use while running the animation as
     * previously set by {@link #setZAdjustment}.
     * 
     * @return Returns one of {@link #ZORDER_NORMAL},
     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
     * @attr ref android.R.styleable#Animation_zAdjustment
     */
    public int getZAdjustment() {
        return mZAdjustment;
    }

    /**
     * Returns the background color behind the animation.
     */
    public int getBackgroundColor() {
        return mBackgroundColor;
    }

    /**
     * Return value of {@link #setDetachWallpaper(boolean)}.
     * @attr ref android.R.styleable#Animation_detachWallpaper
     */
    public boolean getDetachWallpaper() {
        return mDetachWallpaper;
    }

    /**
     * 指示该动画是否会影响转换矩阵,例如淡出效果的动画不会影响矩阵
     *
     * @return true if this animation will change the transformation matrix
     */
    public boolean willChangeTransformationMatrix() {
        // assume we will change the matrix
        return true;
    }

    /**
     *
     * @return true if this animation will change the view's bounds
     */
    public boolean willChangeBounds() {
        // assume we will change the bounds
        return true;
    }

    /**
     * 设置监听
     *
     * @param listener the animation listener to be notified
     */
    public void setAnimationListener(AnimationListener listener) {
        mListener = listener;
    }

    /**
     * 确保动画有一个插值器,如果用户没有设置插值器,该函数将会设置插值器为AccelerateDecelerate
     */
    protected void ensureInterpolator() {
        if (mInterpolator == null) {
            mInterpolator = new AccelerateDecelerateInterpolator();
        }
    }

    /**
     * 计算动画可能持续的时间。可以将时间设置为与该时间不同的时间,通常这个计算出来的时间是比较准确的
     */
    public long computeDurationHint() {
        return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
    }
    
    /**
     * 获取当前时间的转换对象。该方法的实现需要一直将当前对象的转换对象赋值给Transformation。
     * 将当前时间转换为占durationMills的百分比,传递给Transformation。
     *
     * @param currentTime Where we are in the animation. This is wall clock time.
     * @param outTransformation A transformation object that is provided by the
     *        caller and will be filled in by the animation.
     * @return True if the animation is still running
     */
    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mStartTime == -1) {
            mStartTime = currentTime;
        }

        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }

        final boolean expired = normalizedTime >= 1.0f;
        mMore = !expired;

        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            if (!mStarted) {
                fireAnimationStart();
                mStarted = true;
                if (USE_CLOSEGUARD) {
                    guard.open("cancel or detach or getTransformation");
                }
            }

            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

            if (mCycleFlip) {
                normalizedTime = 1.0f - normalizedTime;
            }

            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation);
        }

        if (expired) {
            if (mRepeatCount == mRepeated) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }

        if (!mMore && mOneMoreTime) {
            mOneMoreTime = false;
            return true;
        }

        return mMore;
    }

    private void fireAnimationStart() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationStart(this);
            else mListenerHandler.postAtFrontOfQueue(mOnStart);
        }
    }

    private void fireAnimationRepeat() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationRepeat(this);
            else mListenerHandler.postAtFrontOfQueue(mOnRepeat);
        }
    }

    private void fireAnimationEnd() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationEnd(this);
            else mListenerHandler.postAtFrontOfQueue(mOnEnd);
        }
    }

    /**
     * Gets the transformation to apply at a specified point in time. Implementations of this
     * method should always replace the specified Transformation or document they are doing
     * otherwise.
     *
     * @param currentTime Where we are in the animation. This is wall clock time.
     * @param outTransformation A transformation object that is provided by the
     *        caller and will be filled in by the animation.
     * @param scale Scaling factor to apply to any inputs to the transform operation, such
     *        pivot points being rotated or scaled around.
     * @return True if the animation is still running
     */
    public boolean getTransformation(long currentTime, Transformation outTransformation,
            float scale) {
        mScaleFactor = scale;
        return getTransformation(currentTime, outTransformation);
    }

    /**
     * <p>Indicates whether this animation has started or not.</p>
     *
     * @return true if the animation has started, false otherwise
     */
    public boolean hasStarted() {
        return mStarted;
    }

    /**
     * <p>Indicates whether this animation has ended or not.</p>
     *
     * @return true if the animation has ended, false otherwise
     */
    public boolean hasEnded() {
        return mEnded;
    }

    /**
     *  getTransformation助手,子类应该通过给出插值器来实现这个方法.
     * 
     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
     *        after it has been run through the interpolation function.
     * @param t The Transformation object to fill in with the current
     *        transforms.
     */
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    }

    /**
     * Convert the information in the description of a size to an actual
     * dimension
     *
     * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
     *             Animation.RELATIVE_TO_PARENT.
     * @param value The dimension associated with the type parameter
     * @param size The size of the object being animated
     * @param parentSize The size of the parent of the object being animated
     * @return The dimension to use for the animation
     */
    protected float resolveSize(int type, float value, int size, int parentSize) {
        switch (type) {
            case ABSOLUTE:
                return value;
            case RELATIVE_TO_SELF:
                return size * value;
            case RELATIVE_TO_PARENT:
                return parentSize * value;
            default:
                return value;
        }
    }

    /**
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @param invalidate
     * @param transformation
     * 
     * @hide
     */
    public void getInvalidateRegion(int left, int top, int right, int bottom,
            RectF invalidate, Transformation transformation) {

        final RectF tempRegion = mRegion;
        final RectF previousRegion = mPreviousRegion;

        invalidate.set(left, top, right, bottom);
        transformation.getMatrix().mapRect(invalidate);
        // Enlarge the invalidate region to account for rounding errors
        invalidate.inset(-1.0f, -1.0f);
        tempRegion.set(invalidate);
        invalidate.union(previousRegion);

        previousRegion.set(tempRegion);

        final Transformation tempTransformation = mTransformation;
        final Transformation previousTransformation = mPreviousTransformation;

        tempTransformation.set(transformation);
        transformation.set(previousTransformation);
        previousTransformation.set(tempTransformation);
    }

    /**
     * @param left
     * @param top
     * @param right
     * @param bottom
     *
     * @hide
     */
    public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
        final RectF region = mPreviousRegion;
        region.set(left, top, right, bottom);
        // Enlarge the invalidate region to account for rounding errors
        region.inset(-1.0f, -1.0f);
        if (mFillBefore) {
            final Transformation previousTransformation = mPreviousTransformation;
            applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation);
        }
    }

    protected void finalize() throws Throwable {
        try {
            if (guard != null) {
                guard.warnIfOpen();
            }
        } finally {
            super.finalize();
        }
    }

    /**
     * Return true if this animation changes the view's alpha property.
     * 
     * @hide
     */
    public boolean hasAlpha() {
        return false;
    }

    /**
     * Utility class to parse a string description of a size.
     */
    protected static class Description {
        /**
         * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
         * Animation.RELATIVE_TO_PARENT.
         */
        public int type;

        /**
         * The absolute or relative dimension for this Description.
         */
        public float value;

        /**
         * Size descriptions can appear inthree forms:
         * <ol>
         * <li>An absolute size. This is represented by a number.</li>
         * <li>A size relative to the size of the object being animated. This
         * is represented by a number followed by "%".</li> *
         * <li>A size relative to the size of the parent of object being
         * animated. This is represented by a number followed by "%p".</li>
         * </ol>
         * @param value The typed value to parse
         * @return The parsed version of the description
         */
        static Description parseValue(TypedValue value) {
            Description d = new Description();
            if (value == null) {
                d.type = ABSOLUTE;
                d.value = 0;
            } else {
                if (value.type == TypedValue.TYPE_FRACTION) {
                    d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
                            TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
                                    RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
                    d.value = TypedValue.complexToFloat(value.data);
                    return d;
                } else if (value.type == TypedValue.TYPE_FLOAT) {
                    d.type = ABSOLUTE;
                    d.value = value.getFloat();
                    return d;
                } else if (value.type >= TypedValue.TYPE_FIRST_INT &&
                        value.type <= TypedValue.TYPE_LAST_INT) {
                    d.type = ABSOLUTE;
                    d.value = value.data;
                    return d;
                }
            }

            d.type = ABSOLUTE;
            d.value = 0.0f;

            return d;
        }
    }

    /**
     * <p>An animation listener receives notifications from an animation.
     * Notifications indicate animation related events, such as the end or the
     * repetition of the animation.</p>
     */
    public static interface AnimationListener {
        /**
         * <p>Notifies the start of the animation.</p>
         *
         * @param animation The started animation.
         */
        void onAnimationStart(Animation animation);

        /**
         * <p>Notifies the end of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which reached its end.
         */
        void onAnimationEnd(Animation animation);

        /**
         * <p>Notifies the repetition of the animation.</p>
         *
         * @param animation The animation which was repeated.
         */
        void onAnimationRepeat(Animation animation);
    }
}

常量:

INFINITE: 无限重复动画

RESTART: 当动画需要重复播放时,正向重复

REVERSE:当动画需要重复播放时,反向重复

START_ON_FIRST_FRAME:可以作为动画开始的时间,当getTransformation第一次被调用时启动动画

ABSOLUTE:指定尺寸为绝对像素

RELATIVE_TO_SELF:指定浮动维度,相对于自身,用float*自身宽高

RELATIVE_TO_PARENT:指定浮动维度,相对于父控件,用float*父控件宽高

ZORDER_NORMAL:正在播放的动画保证正常的z轴顺序

ZORDER_TOP:正在播放的动画在z轴最前端

ZORDER_BOTTOM:正在播放的动画在z轴最后端

BOOL变量

boolean mEnded = false; 动画结束时由getTransformation方法设定

boolean mStarted = false;动画开始时由getTransformation方法设定

boolean mCycleFlip = false;当动画重复模式为REVERSE时,由getTransformation方法设定

boolean mInitialized = false;这个值必须由initialize设置为true,表示动画完成初始化可以播放了

boolean mFillBefore = true;动画播放完成(动画开始播放时)是否回到动画开始播放的那一帧,当mFillEnabled=true时才关注这个值

boolean mFillAfter = false;动画播放完成时保持在最后一帧(保持最后状态)

boolean mFillEnabled = false;    决定mFillBefore是否被考虑

变量

long mStartTime = -1;动画开始的毫秒级时间

long mStartOffset; 动画开始的偏移时间

long mDuration;动画播放的持续时间

int mRepeatCount = 0;动画重复次数,默认为一直重复

int mRepeated = 0;动画已经重复了多少次

int mRepeatMode = RESTART;重复模式,RESTART或者REVERSE

private int mZAdjustment;设置z轴模式

private int mBackgroundColor;动画的背景颜色

private float mScaleFactor = 1f;缩放因子

Interpolator mInterpolator;插值器

AnimationListener mListener;监听器,当动画开始,重复,结束是会得到通知

常用方法

XML:android:DetachWallpaper

相关方法  setDetachWallpaper(Boolean)

是否脱离墙纸运行


XML:android:duration

相关方法  setDuration(long)

设置动画运行时间,不能小于0


XML:android:fillAfter

相关方法  setFillAfter(boolean)

如果为true,动画执行完毕后保留在其执行完毕的状态

XML:android:fillBefore

相关方法  setFillBefore(boolean)

如果为true,动画执行完毕后回到动画执行前的状态


XML:android:fillEnable

相关方法  setFillEnable(boolean)

如果为true,考虑fillBefore的状态


XML:android:interpolator

相关方法  setInterpolator(Interpolator)

设置动画的变化速率


XML:android:repeatCount

相关方法 setRepeatCount(int)

设置重复次数


XML:android:repeatMode

相关方法 setRepeatMode(int)

设置重复模式


XML:android:startOffset

相关方法 setStartOffset(long)

设置开始时间偏移量


XML:android:zAdjustment

相关方法 setZAdjustment(int)

设置Z轴顺序模式

常用方法

public boolean getTransformation(long currentTime, Transformation outTransformation)

获取当前时间,并转化为durationMills的百分比,将该百分比传递给Transformation,实现动画

private void reset()

重置动画的初始状态

private void cancel()

取消动画播放,如果设置了监听器,监听器会通知取消状态。如果手动停止动画播放,那么再一次启动播放前,必须调用reset方法。


public boolean isInitialized()
判断初始化是否完成

public void initialize(int width, int height, int parentWidth, int parentHeight)

初始化

width:动画的宽度

height:动画的高度

parentWidth:父控件的宽度

parentHeight:父控件的高度

public void setListenerHandler(Handler handler)

设置监听器句柄


public void restrictDuration(long durationMillis)

确保动画的运行时间小于等于durationMillis,如果大于durationMillis,那么系统会减少重复次数。


public void setStartTime(long startTimeMillis)

设置开始时间,如果时间是START_ON_FIRST_FRAME,调用getTransformation时开始第一次执行


public void start()

将开始时间设为START_ON_FIRST_FRAME的一种快捷写法


public void startNow()

立刻开始

public void setAnimationListener(AnimationListener listener)

设置监听


详细看一下getTransformation 方法


public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mStartTime == -1) {
            mStartTime = currentTime;
        }


        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;

        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }


        final boolean expired = normalizedTime >= 1.0f;
        mMore = !expired;


        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);


        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            if (!mStarted) {
                fireAnimationStart();
                mStarted = true;
                if (USE_CLOSEGUARD) {
                    guard.open("cancel or detach or getTransformation");
                }
            }


            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);


            if (mCycleFlip) {
                normalizedTime = 1.0f - normalizedTime;
            }


            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation);

        }


        if (expired) {
            if (mRepeatCount == mRepeated) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }


                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }


                mStartTime = -1;
                mMore = true;


                fireAnimationRepeat();
            }
        }


        if (!mMore && mOneMoreTime) {
            mOneMoreTime = false;
            return true;
        }


        return mMore;
    }


    private void fireAnimationStart() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationStart(this);
            else mListenerHandler.postAtFrontOfQueue(mOnStart);
        }
    }


    private void fireAnimationRepeat() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationRepeat(this);
            else mListenerHandler.postAtFrontOfQueue(mOnRepeat);
        }
    }


    private void fireAnimationEnd() {
        if (mListener != null) {
            if (mListenerHandler == null) mListener.onAnimationEnd(this);
            else mListenerHandler.postAtFrontOfQueue(mOnEnd);
        }
    }

首先把当前时间转为一个float类型数据,该数据是当前时间占总时间的百分比,然后将该时间传递给Interpolator。该百分比是随时间变化的一个直线型关系,经过插值器转换之后回变成曲线关系,然后将这个转换后的值传递给applyTransformation

applyTransformation

这是一个空方法,具体实现在其子类中,该方法时真正处理动画的过程。看一下其子类的实现方法

RotateAnimation


protected void applyTransformation(float interpolatedTime, Transformation t) {
        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
        float scale = getScaleFactor();
        
        if (mPivotX == 0.0f && mPivotY == 0.0f) {
            t.getMatrix().setRotate(degrees);
        } else {
            t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
        }
    }

可见applyTransformation是实现动画的主要方法,在执行过程中,系统将以非常高的频率来调用该函数,经过在该函数起始位置和结束位置添加获取时间函数,可发现系统每16ms调用一次。

XML:android:fillAfter

相关方法  setFillAfter(boolean)

如果为true,动画执行完毕后保留在其执行完毕的状态

XML:android:fillAfter

相关方法  setFillAfter(boolean)

如果为true,动画执行完毕后保留在其执行完毕的状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值