美文网首页
[Android]属性动画简介(二)属性动画的源码分析

[Android]属性动画简介(二)属性动画的源码分析

作者: dafasoft | 来源:发表于2020-11-27 21:38 被阅读0次

    参考链接:

    属性动画简介(一)属性动画的实现和常用方法

    动画的初始化

    首先ValueAnimator动画的入口开始看起, 以ValueAnimator.ofFloat为例:

    public static ValueAnimator ofFloat(float... values) {
            ValueAnimator anim = new ValueAnimator();
            anim.setFloatValues(values);
            return anim;
        }
    

    代码很简单,new 了一个ValueAnimator对象调用该对象的setFloatValues方法把参数传递到对象中

        public void setFloatValues(float... values) {
            if (values == null || values.length == 0) {
                return;
            }
            if (mValues == null || mValues.length == 0) {
                setValues(PropertyValuesHolder.ofFloat("", values));
            } else {
                PropertyValuesHolder valuesHolder = mValues[0];
                valuesHolder.setFloatValues(values);
            }
            // New property/values/target should cause re-initialization prior to starting
            mInitialized = false;
        }
    

    这里是将可变参数float... values 生成PropertyValuesHolder,在我们初始化ValueAnimator对象后,mValues是null,会走到if分支中,这里这个分支的语句主要是为了防止动画创建后重复设置Value

    还记得我们在上篇中说,使用PropertyValuesHolder和直接指定属性进行初始化实际是一样的么?这里就解答了这个问题,因为不管什么操作,最终都要转换成PropertyValuesHolder

    setValues方法:

        public void setValues(PropertyValuesHolder... values) {
            int numValues = values.length;
            mValues = values;
            mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
            for (int i = 0; i < numValues; ++i) {
                PropertyValuesHolder valuesHolder = values[i];
                mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
            }
            // New property/values/target should cause re-initialization prior to starting
            mInitialized = false;
        }
    

    主要也是一些赋值操作,将PropertyValueHoder按名字保存在map中
    看下PropertyValueHolder的初始化:

            public FloatPropertyValuesHolder(String propertyName, float... values) {
                super(propertyName);
                setFloatValues(values);
            }
    
            @Override
            public void setFloatValues(float... values) {
                super.setFloatValues(values);
                mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
            }
    
    // super.setFloatValues
        public void setFloatValues(float... values) {
            mValueType = float.class;
            mKeyframes = KeyframeSet.ofFloat(values);
        }
    
    // KeyFrameSet#ofFloat
    public static KeyframeSet ofFloat(float... values) {
            boolean badValue = false;
            int numKeyframes = values.length;
            FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
            if (numKeyframes == 1) {
                keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
                keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
                if (Float.isNaN(values[0])) {
                    badValue = true;
                }
            } else {
                keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
                for (int i = 1; i < numKeyframes; ++i) {
                    keyframes[i] =
                            (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                    if (Float.isNaN(values[i])) {
                        badValue = true;
                    }
                }
            }
            if (badValue) {
                Log.w("Animator", "Bad value (NaN) in float animator");
            }
            return new FloatKeyframeSet(keyframes);
        }
    

    这里主要是记录propertyName以及将传入的属性依次转化为KeyFrame并保存在KeyFrameSet中,这个KeyFrame就是上篇文章中提到的关键帧

    在生成KeyFrameSet时,如果入参只有一个,会默认生成一个KeyFrame,比如我们用ValueAnimator.ofInt(100)来创建动画时,系统默认的动画数值是从0-100的

    动画的运行

    动画开始运行的实际是在ValueAnimator#start方法:

    private void start(boolean playBackwards) {
            if (Looper.myLooper() == null) {
                throw new AndroidRuntimeException("Animators may only be run on Looper threads");
            }
            mReversing = playBackwards;
            mSelfPulse = !mSuppressSelfPulseRequested;
            // Special case: reversing from seek-to-0 should act as if not seeked at all.
            if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
                if (mRepeatCount == INFINITE) {
                    // Calculate the fraction of the current iteration.
                    float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                    mSeekFraction = 1 - fraction;
                } else {
                    mSeekFraction = 1 + mRepeatCount - mSeekFraction;
                }
            }
            mStarted = true;
            mPaused = false;
            mRunning = false;
            mAnimationEndRequested = false;
            // Resets mLastFrameTime when start() is called, so that if the animation was running,
            // calling start() would put the animation in the
            // started-but-not-yet-reached-the-first-frame phase.
            mLastFrameTime = -1;
            mFirstFrameTime = -1;
            mStartTime = -1;
            addAnimationCallback(0);
    
            if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
                // If there's no start delay, init the animation and notify start listeners right away
                // to be consistent with the previous behavior. Otherwise, postpone this until the first
                // frame after the start delay.
                startAnimation();
                if (mSeekFraction == -1) {
                    // No seek, start at play time 0. Note that the reason we are not using fraction 0
                    // is because for animations with 0 duration, we want to be consistent with pre-N
                    // behavior: skip to the final value immediately.
                    setCurrentPlayTime(0);
                } else {
                    setCurrentFraction(mSeekFraction);
                }
            }
        }
    

    一些属性值的含义:

    • mReversing 动画是否反向执行
    • mSelfPulse 是否监听动画脉搏
    • mSeekFraction 直接将动画移动到某处(0-1 动画完成度的某个取值)
    • mStarted 是否已开始
    • mPaused 是否暂定
    • mRunning 是否正在运行
    • mAnimationEndRequested 动画是否已经请求中止
    • mLastFrameTime 最后帧时间
    • mFirstFrameTime 第一针时间
    • mStartTime 开始时间

    这个方法的主要工作是初始化动画运行时的一些关键属性,注册动画执行的回调

    当我们试着在这个方法中寻找开始动画的位置时,第一反应一定是startAnimation(); 那我们就进入这个方法看一下:

        private void startAnimation() {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                        System.identityHashCode(this));
            }
    
            mAnimationEndRequested = false;
            initAnimation();
            mRunning = true;
            if (mSeekFraction >= 0) {
                mOverallFraction = mSeekFraction;
            } else {
                mOverallFraction = 0f;
            }
            if (mListeners != null) {
                notifyStartListeners();
            }
        }
    

    notifyStartListeners 主要是调用AnimatorListener#onAnimationStart方法向外界传递动画开始的事件,再看其他逻辑,en....这里好像没有我们期望的东西,接着进入initAnimation看一下:

        void initAnimation() {
            if (!mInitialized) {
                int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    mValues[i].init();
                }
                mInitialized = true;
            }
        }
    

    what!!!! 什么都没有,难道在mValues[i].init() 么?这个mValues是PropertyValueHolder的集合,我们就进入PropertyValueHolder接着看一下init方法:

        void init() {
            if (mEvaluator == null) {
                // We already handle int and float automatically, but not their Object
                // equivalents
                mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                        (mValueType == Float.class) ? sFloatEvaluator :
                        null;
            }
            if (mEvaluator != null) {
                // KeyframeSet knows how to evaluate the common types - only give it a custom
                // evaluator if one has been set on this class
                mKeyframes.setEvaluator(mEvaluator);
            }
        }
    

    还记得上篇中我们讲过,如果我们不指定差值器和估值器的话,系统会默认帮我们生成一个,这里其实就是指定估值器的实现,如果我们没有指定差值器,ValueAnimator会根据数据类型生成对应的默认估值器,这里用的是sFloatEvaluator,它是一个FloatEvaluator对象:

    public class FloatEvaluator implements TypeEvaluator<Number> {
        public Float evaluate(float fraction, Number startValue, Number endValue) {
            float startFloat = startValue.floatValue();
            return startFloat + fraction * (endValue.floatValue() - startFloat);
        }
    }
    

    但是, 我们要寻找的开始动画的入口呢?难道跟游戏一样,有什么隐藏入口?? 这么不讲武德的么?肯定不会是这样的了,问题的关键就在 start方法的addAnimationCallback(0)这一行,看一下实现:

    private void addAnimationCallback(long delay) {
            if (!mSelfPulse) {
                return;
            }
            getAnimationHandler().addAnimationFrameCallback(this, delay);
        }
    

    这里是向AnimationHandler中注册Frame变化的回调,getAnimationHandler()获取的是AnimationHandler的单例对象,如果mSelfPulse为false则不注册

    简单翻译一下AnimationHandler的类说明:

    AnimationHandler是一个自定义的处理类,用来处理所有正在执行的ValueAnimator的共享时间脉冲回调,通过这种方式能确保动画值得设定可以和动画开始时的线程相同,并且所有的动画共享相同的时间来计算值,实现了动画的同步
    AnimationHandler默认使用Choreographer 来做周期回调,如果想使用独立的UI Frame更新来提供时间脉冲,可以再hander中设置一个自定义的AnimationFrameCallbackProvider,这在测试中很有用

    首先看一下AnimationHandler实例的获取:

        public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
        private boolean mListDirty = false;
    
        public static AnimationHandler getInstance() {
            if (sAnimatorHandler.get() == null) {
                sAnimatorHandler.set(new AnimationHandler());
            }
            return sAnimatorHandler.get();
        }
    

    这里看到一个熟悉的身影:ThreadLocal,这个在Hander中用的特别多,就不展开讨论了,看到这里大家肯定能理解为什么通过AnimationHandlerk可以保证线程的同步了,原来是每个线程都会有一个实例啊!

    Choreographer 是做什么的呢?

    Choreographer 是在Android 4.1引入的,这个类用来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。其实UI显示的时候每一帧要完成的事情只有这三种

    Choreographer接收显示系统的时间脉冲(垂直同步信号-VSync信号),在下一个frame渲染时控制执行这些操作。

    Choreographer中文翻译过来是"舞蹈指挥",字面上的意思就是优雅地指挥以上三个UI操作一起跳一支舞。这个词可以概括这个类的工作,如果android系统是一场芭蕾舞,他就是Android UI显示这出精彩舞剧的编舞,指挥台上的演员们相互合作,精彩演出

    至于VSYNC是什么,这里是另外一个宏大话题了,这里我们以后再分析,这里不展开讨论了,我们现在只需要知道他是设备硬件发出的脉冲信号即可,它设计的初衷是为了让Android的UI体验“像黄油一样润滑”

    Choreographer的实例也是保存在ThreadLocal中,因此,它也是线程中唯一的。具体创建和销毁过程我们以后再分析

    OK 我们接着分析AnimationHandler的源码

    刚才我们讲到在ValueAnimator#addAnimationCallback方法中调用了AnimationHandler#addAnimationFrameCallback

        public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
            if (mAnimationCallbacks.size() == 0) {
    
                getProvider().postFrameCallback(mFrameCallback);
            }
            if (!mAnimationCallbacks.contains(callback)) {
                mAnimationCallbacks.add(callback);
            }
    
            if (delay > 0) {
                mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
            }
        }
    

    这个方法主要是向Choreographer注册VSYNC脉冲的监听,如果mAnimationCallbacks的size为0,说明本线程没有向Choreographer注册过,则把mFrameCallback发送给Choreographer,如果已经注册过了,只需要向mAnimationCallbacks中添加即可

    getProvider是获取的MyFrameCallbackProvider的实例,MyFrameCallbackProvider类如下:

    private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
    
            final Choreographer mChoreographer = Choreographer.getInstance();
    
            @Override
            public void postFrameCallback(Choreographer.FrameCallback callback) {
                mChoreographer.postFrameCallback(callback);
            }
    
            @Override
            public void postCommitCallback(Runnable runnable) {
                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
            }
    
            @Override
            public long getFrameTime() {
                return mChoreographer.getFrameTime();
            }
    
            @Override
            public long getFrameDelay() {
                return Choreographer.getFrameDelay();
            }
    
            @Override
            public void setFrameDelay(long delay) {
                Choreographer.setFrameDelay(delay);
            }
        }
    

    那么mFrameCallback是什么呢?看一下:

    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                doAnimationFrame(getProvider().getFrameTime());
                if (mAnimationCallbacks.size() > 0) {
                    getProvider().postFrameCallback(this);
                }
            }
        };
    

    这里是接口Choreographer.FrameCallback的一个实现,作用是处理Choreographer传递的脉冲事件,处理完成后如果mAnimationCallbacks的size不为0,则继续向Choreographer发送mFrameCallback自身

    从这里就转入Choreograhper中执行,最终调用到Choreograhper#postFrameCallbackDelayed:

        public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
            if (callback == null) {
                throw new IllegalArgumentException("callback must not be null");
            }
    
            postCallbackDelayedInternal(CALLBACK_ANIMATION,
                    callback, FRAME_CALLBACK_TOKEN, delayMillis);
        }
    

    这里就是向Choreograhper注册类型为CALLBACK_ANIMATION的回调,Choreograhper的回调类型有四种,
    分别是

    • INPUT:输入事件
    • ANIMATION:动画
    • TRAVERSAL:窗口刷新,执行measure/layout/draw操作
    • COMMIT:遍历完成的提交操作,用来修正动画启动时间

    这里不多讲,我们只需要知道当有VSYNC的脉冲信号传来,Choreograhper会回调到Choreographer.FrameCallback,最终会回调到AnimationHandler中的mFrameCallback的doFrame

    doFrame会执行doAnimationFrame(getProvider().getFrameTime());

        private void doAnimationFrame(long frameTime) {
            long currentTime = SystemClock.uptimeMillis();
            final int size = mAnimationCallbacks.size();
            for (int i = 0; i < size; i++) {
                final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
                if (callback == null) {
                    continue;
                }
                if (isCallbackDue(callback, currentTime)) {
                    callback.doAnimationFrame(frameTime);
                    if (mCommitCallbacks.contains(callback)) {
                        getProvider().postCommitCallback(new Runnable() {
                            @Override
                            public void run() {
                                commitAnimationFrame(callback, getProvider().getFrameTime());
                            }
                        });
                    }
                }
            }
            cleanUpList();
        }
    

    这里会循环调用在AnimationHandler中保存的AnimationFrameCallback的doAnimationFrame方法,这个方法的实现在ValueAnimator中:

    public final boolean doAnimationFrame(long frameTime) {
            if (mStartTime < 0) {
                mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);
            }
    
            if (mPaused) {
                mPauseTime = frameTime;
                removeAnimationCallback();
                return false;
            } else if (mResumed) {
                mResumed = false;
                if (mPauseTime > 0) {
                    mStartTime += (frameTime - mPauseTime);
                }
            }
    
            if (!mRunning) {
                if (mStartTime > frameTime && mSeekFraction == -1) {
                    return false;
                } else {
                    mRunning = true;
                    startAnimation();
                }
            }
    
            if (mLastFrameTime < 0) {
                if (mSeekFraction >= 0) {
                    long seekTime = (long) (getScaledDuration() * mSeekFraction);
                    mStartTime = frameTime - seekTime;
                    mSeekFraction = -1;
                }
                mStartTimeCommitted = false; // allow start time to be compensated for jank
            }
            mLastFrameTime = frameTime;
            final long currentTime = Math.max(frameTime, mStartTime);
            boolean finished = animateBasedOnTime(currentTime);
            if (finished) {
                endAnimation();
            }
            return finished;
        }
    

    这里一系列的逻辑,比如运行时间的更新、动画结束的判定、动画开始的判定等等,其中动画运行的更新是在animateBasedOnTime方法:

       boolean animateBasedOnTime(long currentTime) {
            boolean done = false;
            if (mRunning) {
                final long scaledDuration = getScaledDuration();
                // 根据开始时间、运行时间和动画总时间计算初始fraction
                final float fraction = scaledDuration > 0 ?
                        (float)(currentTime - mStartTime) / scaledDuration : 1f;
                final float lastFraction = mOverallFraction;
                final boolean newIteration = (int) fraction > (int) lastFraction;
                final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                        (mRepeatCount != INFINITE);
                if (scaledDuration == 0) {
                    // 0 duration animator, ignore the repeat count and skip to the end
                    done = true;
                } else if (newIteration && !lastIterationFinished) {
                    // Time to repeat
                    if (mListeners != null) {
                        int numListeners = mListeners.size();
                        for (int i = 0; i < numListeners; ++i) {
                            mListeners.get(i).onAnimationRepeat(this);
                        }
                    }
                } else if (lastIterationFinished) {
                    done = true;
                }
                mOverallFraction = clampFraction(fraction);
                // 根据初始fraction和动画的缩放条件、运行次数等计算出变换过的初始fraction
                float currentIterationFraction = getCurrentIterationFraction(
                        mOverallFraction, mReversing);
    
                animateValue(currentIterationFraction);
            }
            return done;
        }
    

    这个方法主要是计算出初始的fraction,即动画完成度,如果属性动画不支持Interpolator,那么关于动画完成度的变换在这里就截止了,但很明显并非如此,在计算过currentIterationFraction 后执行了animateValue方法:

            void animateValue(float fraction) {
            // 使用自定义的Interpolator计算fraction
            fraction = mInterpolator.getInterpolation(fraction);
            mCurrentFraction = fraction;
            int numValues = mValues.length;
          // 遍历该动画的PropertyValueHolder
            for (int i = 0; i < numValues; ++i) {
                // 将fraction传入入PropertyValueHolder中计算
                mValues[i].calculateValue(fraction);
            }
            if (mUpdateListeners != null) {
                int numListeners = mUpdateListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    mUpdateListeners.get(i).onAnimationUpdate(this);
                }
            }
        }
    

    这里计算出的fraction就是我们最终在TypeEvaluator中使用的fraction,它是经过自定义Interpolator转换的,如果我们在创建动画时没有指定Interpolator,系统默认使用AccelerateDecelerateInterpolator:

        private static final TimeInterpolator sDefaultInterpolator =
                new AccelerateDecelerateInterpolator();
    

    在ValueAnimator#animateValue中遍历PropertyValueHolder后,计算工作转入PropertyValueHolder#calculateValue方法,calculateValue调用KeyFrames#getValue,KeyFrames是一个接口,因为我们创建的是一个Float型的ValueAnimator,它最终的实现是在FloatKeyframeSet#getValue:

    public float getFloatValue(float fraction) {
            if (fraction <= 0f) {
                final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
                final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
                float prevValue = prevKeyframe.getFloatValue();
                float nextValue = nextKeyframe.getFloatValue();
                float prevFraction = prevKeyframe.getFraction();
                float nextFraction = nextKeyframe.getFraction();
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                if (interpolator != null) {
                    fraction = interpolator.getInterpolation(fraction);
                }
                float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
                return mEvaluator == null ?
                        prevValue + intervalFraction * (nextValue - prevValue) :
                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                                floatValue();
            } else if (fraction >= 1f) {
                final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
                final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
                float prevValue = prevKeyframe.getFloatValue();
                float nextValue = nextKeyframe.getFloatValue();
                float prevFraction = prevKeyframe.getFraction();
                float nextFraction = nextKeyframe.getFraction();
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                if (interpolator != null) {
                    fraction = interpolator.getInterpolation(fraction);
                }
                float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
                return mEvaluator == null ?
                        prevValue + intervalFraction * (nextValue - prevValue) :
                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                                floatValue();
            }
            FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
            for (int i = 1; i < mNumKeyframes; ++i) {
                FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
                if (fraction < nextKeyframe.getFraction()) {
                    final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                    float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                        (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                    float prevValue = prevKeyframe.getFloatValue();
                    float nextValue = nextKeyframe.getFloatValue();
                    // Apply interpolator on the proportional duration.
                    if (interpolator != null) {
                        intervalFraction = interpolator.getInterpolation(intervalFraction);
                    }
                    return mEvaluator == null ?
                            prevValue + intervalFraction * (nextValue - prevValue) :
                            ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                                floatValue();
                }
                prevKeyframe = nextKeyframe;
            }
            // shouldn't get here
            return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
        }
    

    这个方法的返回值是我们在调用ValueAnimator#getAnimatedValue时获取到的值,当fracion小于等于0时,intervalFraction 使用第一个KeyFrame和第二个KeyFrame计算,当fraction大于等于1时,intervalFraction使用最后一个和倒数第二个KeyFrame计算,因此当fraction小于0或者大于1时,有可能计算的值不是我么预期的,需谨慎使用,其他情况fraction的处理都是使用当前KeyFrame和下一个KeyFrame进行计算

    当计算出intervalFraction后会调用估值器mEvaluator进行动画值的计算,计算出的值会赋给PropertyValueHolder的属性mAnimatedValue中,当调用ValueAnimator#getAnimatedValue是,会将这个值传递出去:

         /*** ValueAnimator */
        public Object getAnimatedValue() {
            if (mValues != null && mValues.length > 0) {
                return mValues[0].getAnimatedValue();
            }
            // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
            return null;
        }
    

    其中mValues[0]就是PropertyValueHolder

         /*** PropertyValueHolder*/
        Object getAnimatedValue() {
            return mAnimatedValue;
        }
    

    回顾一下我们在上一篇介绍过的一个案例:

    上面的规定没完没了,现在要求View从50匀速移动到400,在动画运行的前半段透明度为100% 后半段从100%变为0%

    在上篇中我们使用了KeyFrame来实现这一需求,当我们分析过源码后,是不是能理解为什么可以这么做了?

    通过上面的分析,我们大概捋清了动画的开始和运行机制

    可以大概得出以下结论:

    • ValueAnimator#start方法的执行并不代表动画的真正运行,它的作用是初始化属性和注册帧变化的监听,动画的真正运行在系统绘制下一帧的脉冲信号到来时
    • 对于一个动画来说, 差值器和估值器是不可缺少的组件,如果我们没有手动声明,系统会使用默认值
    • 动画值得计算并非由动画本身完成,而是由ProperyValueHolder完成,ProperyValueHolder顾名思义,是用来持有动画所改变的属性及其取值
    • 如果我们创建多个动画,无论动画是否在一个线程,创建时间是否有差异,它们的刷新都是在同一时机由系统统一处理
    • Interpolator和Evaluator指定了调用方式和时机,但是具体实现完全由使用者控制,这是一种很典型的策略模式

    动画的结束

    动画的结束判断在ValueAnimator#doAnimationFrame方法中:

    public final boolean doAnimationFrame(long frameTime) {
            if (mStartTime < 0) {
                // First frame. If there is start delay, start delay count down will happen *after* this
                // frame.
                mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);
            }
    
            // Handle pause/resume
            if (mPaused) {
                mPauseTime = frameTime;
                removeAnimationCallback();
                return false;
            } else if (mResumed) {
                mResumed = false;
                if (mPauseTime > 0) {
                    // Offset by the duration that the animation was paused
                    mStartTime += (frameTime - mPauseTime);
                }
            }
    
            if (!mRunning) {
                // If not running, that means the animation is in the start delay phase of a forward
                // running animation. In the case of reversing, we want to run start delay in the end.
                if (mStartTime > frameTime && mSeekFraction == -1) {
                    // This is when no seek fraction is set during start delay. If developers change the
                    // seek fraction during the delay, animation will start from the seeked position
                    // right away.
                    return false;
                } else {
                    // If mRunning is not set by now, that means non-zero start delay,
                    // no seeking, not reversing. At this point, start delay has passed.
                    mRunning = true;
                    startAnimation();
                }
            }
    
            if (mLastFrameTime < 0) {
                if (mSeekFraction >= 0) {
                    long seekTime = (long) (getScaledDuration() * mSeekFraction);
                    mStartTime = frameTime - seekTime;
                    mSeekFraction = -1;
                }
                mStartTimeCommitted = false; // allow start time to be compensated for jank
            }
            mLastFrameTime = frameTime;
            // The frame time might be before the start time during the first frame of
            // an animation.  The "current time" must always be on or after the start
            // time to avoid animating frames at negative time intervals.  In practice, this
            // is very rare and only happens when seeking backwards.
            final long currentTime = Math.max(frameTime, mStartTime);
            boolean finished = animateBasedOnTime(currentTime);
    
            if (finished) {
                endAnimation();
            }
            return finished;
        }
    

    animateBasedOnTime方法的返回值finished 就是动画是否需要结束的标记,它的判断条件就是动画运行的物理时间是否大于等于我们设置的动画duration,代码比较简单就不分析了。

    相关文章

      网友评论

          本文标题:[Android]属性动画简介(二)属性动画的源码分析

          本文链接:https://www.haomeiwen.com/subject/rvntwktx.html