属性动画ValueAnimator源码解析

先来看一段代码,如下:

ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);  

        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {  

            @Override  
            public void onAnimationUpdate(ValueAnimator animator) {  
                //获得当前动画的进度值,整型,1-100之间  
        int currentValue = (Integer)animator.getAnimatedValue();  

            }  
        });  
        valueAnimator.setDuration(450).start();  

当时写这段代码是为了慢慢的从屏幕的底部展示一个不断上升的View,当然这只是其中部分代码,当时不知道整个流程是怎样的,getAnimatedValue 是怎么获取值的,今天有时间就把源码撸了遍,然后记录下来。

上面的代码,我们通过ValueAnimator.ofInt方法来设置动画的起始和停止值,如果只设置一个那么默认的起始值为0(这个等后面的源码来分析),
通过设置更新进度的监听来获取当前的动画进行的进度(1~100);
valueAnimator.start()来执行动画,如果不设置动画的执行时间,那么默认的是300ms,见源码分析。

先来看看ofInt的代码:

public static ValueAnimator ofInt(int... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setIntValues(values);
        return anim;
    }

很简单,就是new 一个ValueAnimator,并把我们设置的值传递进去,然后返回一个ValueAnimator对象,setIntValues代码如下:

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

第一条判断条件很简单,传递进来的值为空或者数据为空则return;
第二个条件中mValues 指的是啥呢? PropertyValuesHolder[] mValues; //这个类表示传递数据的属性和值,即是int型、float还是其他什么类型的值;

一开始 mValues 是null,所以条件成立,执行 setValues 方法,传入 setValues 中的参数 由 PropertyValuesHolder.ofInt 方法获取,

  public static PropertyValuesHolder ofInt(String propertyName, int... values) {
    return new IntPropertyValuesHolder(propertyName, values);
    }

该方法返回IntPropertyValuesHolder 对象,IntPropertyValuesHolder 是 PropertyValuesHolder 的静态内部类并且继承自 PropertyValuesHolder ,

 public IntPropertyValuesHolder(Property property, int... values) {
            super(property);
            setIntValues(values);
            if (property instanceof  IntProperty) {
                mIntProperty = (IntProperty) mProperty;
            }
        }

这个我们只要看看setIntValues(values);这个我们只要看看setIntValues(values);

@Override
public void setIntValues(int… values) {
super.setIntValues(values);
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
}
父类的 setIntValues 都做了哪些事呢?

 public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframeSet = KeyframeSet.ofInt(values);
    }

为变量 mValueType 赋值为 int.class; KeyframeSet.ofInt(values)这个方法就是执行动画赋值操作,

public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

numKeyframes 表示传递给动画值的个数,keyframes 的最小个数是2;
看看Keyframe.ofInt的两个方法:

IntKeyframe(float fraction, int value) {
            mFraction = fraction;
            mValue = value;
            mValueType = int.class;
            mHasValue = true;
        }
IntKeyframe(float fraction) {
            mFraction = fraction;
            mValueType = int.class;
        }

可以看出,如果传递过来的数值个数是1的话,keyframes[0] 只设置了 mFraction 而没有设置 mValue;
如果个数大于等于2的话,都会设置值进去,然后返回一个 IntKeyframeSet 对象:

public IntKeyframeSet(IntKeyframe... keyframes) {
        super(keyframes);
    }

IntKeyframeSet 是 KeyframeSet 的子类,看看 KeyframeSet 中的构造方法:

 public KeyframeSet(Keyframe... keyframes) {
        mNumKeyframes = keyframes.length;
        mKeyframes = new ArrayList<Keyframe>();
        mKeyframes.addAll(Arrays.asList(keyframes));
        mFirstKeyframe = mKeyframes.get(0);
        mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
        mInterpolator = mLastKeyframe.getInterpolator();
    }

mNumKeyframes 表示 Keyframe的个数,也就是传递的int型数据的个数,我们这里传递的是(1,100),所以 mNumKeyframes =2;
mKeyframes将之前设置数据的 Keyframe 放入集合,mFirstKeyframe表示第一个数据, mLastKeyframe 表示最后一个数据;
mInterpolator 表示最后一个数据所传递的插值器;

再回到 IntPropertyValuesHolder 的 setIntValues方法

 @Override
        public void setIntValues(int... values) {
            super.setIntValues(values);
            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
        }

最后 将获取的数据 赋值给 mIntKeyframeSet;
再回过头来看看 ValueAnimator 的 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 = (PropertyValuesHolder) values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

参数就是之前通过一系列的步骤获取的;这个方法很简单,就是 将值 赋给 mValues,然后通过键值对方式放入map中。

好啦,到现在,设置动画值的操作终于走完啦,下面来看看start方法啦:

  @Override
    public void start() {
        start(false);
    }

// playBackwards 表示是否需要反过来执行动画,默认为false,但也可以 reverse 方法来设置;
//这个方法是运行在调用这个方法的线程中。

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mPlayingBackwards = playBackwards;
        mCurrentIteration = 0;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        sPendingAnimations.get().add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            setCurrentPlayTime(getCurrentPlayTime());
            mPlayingState = STOPPED;
            mRunning = true;

            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationStart(this);
                }
            }
        }
        AnimationHandler animationHandler = sAnimationHandler.get();
        if (animationHandler == null) {
            animationHandler = new AnimationHandler();
            sAnimationHandler.set(animationHandler);
        }
        animationHandler.sendEmptyMessage(ANIMATION_START);
    }

开始是设置几个状态值:
mPlayingState = STOPPED; //暂停状态
mStarted = true;
mStartedDelay = false;
sPendingAnimations.get().add(this);

//mPlayingState 有以下3种取值
static final int STOPPED = 0; // Not yet playing
static final int RUNNING = 1; // Playing normally
static final int SEEKED = 2; // Seeked to some time value

mStartDelay 如果不设置延迟的话,默认值是0,然后走到 setCurrentPlayTime(getCurrentPlayTime()),getCurrentPlayTime就是获取当前的系统时间
    public long getCurrentPlayTime() {
        if (!mInitialized || mPlayingState == STOPPED) {
            return 0;
        }
        return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
    }

上面代码的逻辑很简单,就是 未初始化或者 mPlayingState 为STOPPED的时候返回0,否则返回 系统当前时间减去动画开始时间;

   public void setCurrentPlayTime(long playTime) {
        initAnimation();
        long currentTime = AnimationUtils.currentAnimationTimeMillis();
        if (mPlayingState != RUNNING) {
            mSeekTime = playTime;
            mPlayingState = SEEKED;
        }
        mStartTime = currentTime - playTime;
        animationFrame(currentTime);
    }

首先 初始化动画,

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

由于开始是 mInitialized 是false且mValues之前也赋值过,所以走到 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
            mKeyframeSet.setEvaluator(mEvaluator);
        }
    }
这个方法就是为了设置 Evaluator的,由于我们传进来的数据类型是 int.class,所以 mEvaluator = null;
在  initAnimation 方法的最后 mInitialized = true; 回到 setCurrentPlayTime 方法中,由于 开始 mPlayingState = STOPPED,  满足条件,

执行 mSeekTime = playTime; mPlayingState = SEEKED; mStartTime = currentTime - playTime; 最后执行 animationFrame方法:

  boolean animationFrame(long currentTime) {
        boolean done = false;

        if (mPlayingState == STOPPED) {
            mPlayingState = RUNNING;
            if (mSeekTime < 0) {
                mStartTime = currentTime;
            } else {
                mStartTime = currentTime - mSeekTime;
                // Now that we're playing, reset the seek time
                mSeekTime = -1;
            }
        }
        switch (mPlayingState) {
        case RUNNING:
        case SEEKED:
            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
            if (fraction >= 1f) {
                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                    // Time to repeat
                    if (mListeners != null) {
                        int numListeners = mListeners.size();
                        for (int i = 0; i < numListeners; ++i) {
                            mListeners.get(i).onAnimationRepeat(this);
                        }
                    }
                    if (mRepeatMode == REVERSE) {
                        mPlayingBackwards = mPlayingBackwards ? false : true;
                    }
                    mCurrentIteration += (int)fraction;
                    fraction = fraction % 1f;
                    mStartTime += mDuration;
                } else {
                    done = true;
                    fraction = Math.min(fraction, 1.0f);
                }
            }
            if (mPlayingBackwards) {
                fraction = 1f - fraction;
            }
            animateValue(fraction);
            break;
        }

        return done;
    }
这就是执行动画的方法, 开始 done = false;由于 mPlayingState 之前赋值为 SEEKED,所以 走到 case 为 SEEKED 的代码块中,第一句中,由于我们设置了 动画的持续时间
所以 mDuration 大于0,如果不设置, mDuration默认为 300ms, //private long mDuration = 300;
从上面的 setCurrentPlayTime 方法中,我们可以知道 currentTime = mStartTime, 所以 执行到 if (mPlayingBackwards) ,而 mPlayingBackwards=false,直接执行 animateValue(fraction)
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

首先通过插值器获取 fraction, 默认的插值器是 AccelerateDecelerateInterpolator,这个getInterpolation代码很简单,就是通过余弦函数来取值,不说,跳过去;
看 mValues 的calculateValue 方法:

 void calculateValue(float fraction) {
        mAnimatedValue = mKeyframeSet.getValue(fraction);
    }

getValue调用的是 IntKeyframeSet中的 getIntValue方法:

 public int getIntValue(float fraction) {
        if (mNumKeyframes == 2) {
            if (firstTime) {
                firstTime = false;
                firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
                lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
                deltaValue = lastValue - firstValue;
            }
            if (mInterpolator != null) {
                fraction = mInterpolator.getInterpolation(fraction);
            }
            if (mEvaluator == null) {
                return firstValue + (int)(fraction * deltaValue);
            } else {
                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
            }
        }
       ......
    }

这个代码很长,截取部分代码, firstValue就是我们之前传入的1, lastValue是 100;mEvaluator之前也判断了是null,然后return 返回值;
回到 animateValue 方法,设置了 mAnimatedValue 值后,通过我们 add的 UpdateListeners 的 onAnimationUpdate 回调中 就可以通过 animator.getAnimatedValue() 来获取当前的进度值啦,

这样就完啦?。。。。没有的时,这才执行了部分进度,还没完成呢,start(boolean )方法还没执行完

该代码还剩下面的 :

 ......
   AnimationHandler animationHandler = sAnimationHandler.get();
        if (animationHandler == null) {
            animationHandler = new AnimationHandler();
            sAnimationHandler.set(animationHandler);
        }
        animationHandler.sendEmptyMessage(ANIMATION_START);

就是通过handler来循环执行动画,

public void handleMessage(Message msg) {
            boolean callAgain = true;
            ArrayList<ValueAnimator> animations = sAnimations.get();
            ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
            switch (msg.what) {
                // TODO: should we avoid sending frame message when starting if we
                // were already running?
                case ANIMATION_START:
                    ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
                    if (animations.size() > 0 || delayedAnims.size() > 0) {
                        callAgain = false;
                    }

                    while (pendingAnimations.size() > 0) {
                        ArrayList<ValueAnimator> pendingCopy =
                                (ArrayList<ValueAnimator>) pendingAnimations.clone();
                        pendingAnimations.clear();
                        int count = pendingCopy.size();
                        for (int i = 0; i < count; ++i) {
                            ValueAnimator anim = pendingCopy.get(i);
                            // If the animation has a startDelay, place it on the delayed list
                            if (anim.mStartDelay == 0) {
                                anim.startAnimation();
                            } else {
                                delayedAnims.add(anim);
                            }
                        }
                    }
                    // fall through to process first frame of new animations
                case ANIMATION_FRAME:
            ......
            ......
                    int numAnims = animations.size();
                    int i = 0;
                    while (i < numAnims) {
                        ValueAnimator anim = animations.get(i);
                        if (anim.animationFrame(currentTime)) {
                            endingAnims.add(anim);
                        }
                        if (animations.size() == numAnims) {
                            ++i;
                        } else {

                            --numAnims;
                            endingAnims.remove(anim);
                        }
                    }
                    if (endingAnims.size() > 0) {
                        for (i = 0; i < endingAnims.size(); ++i) {
                            endingAnims.get(i).endAnimation();
                        }
                        endingAnims.clear();
                    }

                    // If there are still active or delayed animations, call the handler again
                    // after the frameDelay
                    if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
                        sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
                            (AnimationUtils.currentAnimationTimeMillis() - currentTime)));
                    }
                    break;
            }
        }

传过来的 msg.what = ANIMATION_START, pendingAnimations 之前对 sPendingAnimations 赋值了, 所以pendingAnimations size >0; 执行到 anim.startAnimation()

private void startAnimation() {
        initAnimation();
        sAnimations.get().add(this);
        if (mStartDelay > 0 && mListeners != null) {
            // Listeners were already notified in start() if startDelay is 0; this is
            // just for delayed animations
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationStart(this);
            }
        }
    }

initAnimation()方法之前讲过,因为 mInitialized 之前被赋值为true,所以 直接退出该方法;sAnimations add当前对象即 ValueAnimator;mStartDelay = 0,该方法执行完;
因为 pendingAnimations.clear(); 所以 while 循环结束;

执行到 case ANIMATION_FRAME: int numAnims = animations.size(); 之前 sAnimations add 一个ValueAnimator,所以 animationFrame,这个方法之前讲过,知道 时间执行完后

把该方法的部分代码拿过来,如下,

case SEEKED:
            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
            if (fraction >= 1f) {
                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                    // Time to repeat
                    if (mListeners != null) {
                        int numListeners = mListeners.size();
                        for (int i = 0; i < numListeners; ++i) {
                            mListeners.get(i).onAnimationRepeat(this);
                        }
                    }
                    if (mRepeatMode == REVERSE) {
                        mPlayingBackwards = mPlayingBackwards ? false : true;
                    }
                    mCurrentIteration += (int)fraction;
                    fraction = fraction % 1f;
                    mStartTime += mDuration;
                } else {
                    done = true;
                    fraction = Math.min(fraction, 1.0f);
                }
            }
            if (mPlayingBackwards) {
                fraction = 1f - fraction;
            }
            animateValue(fraction);
            break;
        }

只有 动画执行时间 大于 mDuration, fraction才会>=1,因为不需要循环,所以 done = true; animateValue 之前说过就是设置当前进度,然后通过回调获取该进度;

animationFrame 方法返回ture, 所以会调用 刚才handler中的handMessage 方法 ndingAnims.add(anim); 最后 执行 endAnimation来结束动画,并资源释放。

如果动画执行时间还没到,会 sendEmptyMessageDelayed 来循环执行。

好啦,整个流程讲完啦。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值