属性动画源码探究

在Anroid 3.0之前,Android系统给我们提供了两种动画效果方式:补间动画和帧动画,3.0之后有了属性动画,属性动画与前两者的区别在于属性动画是改变的View的实际属性。比如我们改变一个Button的位置,用属性动画平移后,View是可以响应点击事件的。而用前面两种动画则不行。
属性动画我们经常用到的是ObjectAnimator.ofFloat方法,今天我们就从这个方法入手看看源码:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }

首先是新建一个ObjectAnimator实例,讲我们传进来的view和要操作的属性名保存为ObjectAnimator的全局变量。然后是对我们传入的values进行层层封装。
看看anim.setFloatValues方法的实现。

@Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

mValues是PropertyValuesHolder[] 数组,mProperty是Property类的实例,它们都还没有被初始化,所以都为null,所以下面会调用setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));这行代码。
进入该方法:

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;
    }

这个方法的最终目的就是给将PropertyValuesHolder存入到mValuesMap里面,以我们传入的属性名作为key,而我们传入的values这时已经被封装到了PropertyValuesHolder里面,是通过PropertyValuesHolder.ofFloat(mPropertyName, values)这行代码进行封装的,进入PropertyValuesHolder的ofFloat方法,

public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }

发现新建了一个FloatPropertyValuesHolder,将我们属性名mPropertyName和Values传了进去。很明显FloatPropertyValuesHolder是PropertyValuesHolder的子类。

public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
        }

在构造方法里接着调用了setFloatValues方法,

@Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

接着会调用父类的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);
    }

这段代码很好理解,先创建了一个FloatKeyframe类型的keyframes[] 数组,接着看下面的for循环里,讲values通过Keyframe.ofFloat方法封装到了FloatKeyframe里面,然后添加到keyframes[] 数组里。最后将keyframes数组作为参数传到FloatKeyframeSet里。

回过头来再想一想,上面的这些就是对我们通过ObjectAnimator.ofFloat()方法传入的values进行了层层封装,最后封装到了FloatKeyframe这个关键帧里面,FloatKeyframeSet(的父类KeyframeSet)持有FloatKeyframe的引用,PropertyValuesHolder持有KeyframeSet的引用,而PropertyValueHolder又被存入到了ObjectAnimator(的父类ValueAnimator)的mValuesMap一个map里。
一些初始化的工作完成后,我们就会调用start方法开始执行动画,那么我们看看ObjectAnimator的start方法,调用了父类(ValueAnimator)的start方法,

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        // 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 = 0;
        AnimationHandler animationHandler = AnimationHandler.getInstance();
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

        if (mStartDelay == 0 || mSeekFraction >= 0) {
            // 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);
            }
        }
    }

有这么两行代码
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
执行到了这里会有一个callback回调,传入的参数是this,也就是说ValueAnimator类肯定实现了这个callback,我们去看一下 确实是实现了AnimationHandler.AnimationFrameCallback接口,那么找它的实现方法doAnimationFrame

public final void doAnimationFrame(long frameTime) {
        AnimationHandler handler = AnimationHandler.getInstance();
        if (mLastFrameTime == 0) {
            // First frame
            handler.addOneShotCommitCallback(this);
            if (mStartDelay > 0) {
                startAnimation();
            }
            if (mSeekFraction < 0) {
                mStartTime = frameTime;
            } else {
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
            }
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        mLastFrameTime = frameTime;
        if (mPaused) {
            mPauseTime = frameTime;
            handler.removeCallback(this);
            return;
        } else if (mResumed) {
            mResumed = false;
            if (mPauseTime > 0) {
                // Offset by the duration that the animation was paused
                mStartTime += (frameTime - mPauseTime);
                mStartTimeCommitted = false; // allow start time to be compensated for jank
            }
            handler.addOneShotCommitCallback(this);
        }
        // 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();
        }
    }

这里首先会执行startAnimation()方法,后面还会执行animateBasedOnTime()方法,
线点进去看看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();
        }
    }

我们看到有个initAnimation()方法,注意这里有个坑,按cmd键点进去这是ValueAninmator的initAnimation()方法,暂且先看一下这个方法:

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

调用了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);
        }
    }

我们的估值器TypeEvaluator就是在PropertyValueHolder的init方法里被设置进去的。

然而不应该执行到就结束了啊 我们还没真正的找到动画是怎么执行的呢!!执行动画起码应该是对View做点什么吧。。。我们传进来的参数被封装了也没看到怎么用的呢?
所以我们找错代码了… 坑就在上面刚说的initAnimation()这个方法。父类ValueAnimator有这个方法,但是子类ObjectAnimator也有这个方法。
我们进入ObjectAnimator的initAnimation()方法里看看 ,。

@CallSuper
    @Override
    void initAnimation() {
        if (!mInitialized) {
            // mValueType may change due to setter/getter setup; do this before calling super.init(),
            // which uses mValueType to set up the default type evaluator.
            final Object target = getTarget();
            if (target != null) {
                final int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            super.initAnimation();
        }
    }

for循环里面调用了PropertyValueHolder的setupSetterAndGetter(target)方法,找到这个方法target就是我们传入的view,而看这个方法名里有setter和getter,我们猜可能是会调用view的set和get方法。其实我理解这个方法的最终目的就是给PropertyValueHolder的两个成员变量mSetter和mGetter赋值的。这两个都是Method的实例,如果我们想通过反射去调用view的setXXX()和getXXX()方法,我们就要拿到Method,然后调用Method的invoke()方法。当然我们还需要知道方法名,比如View.setTranslationX()方法。我们使用ObjectAnimator.ofFloat(view,”translationX”,values),这里传入的translationX就是用来获取方法名的,源码里是通过getMethodName方法来获取的实现方式如下:

String methodName = getMethodName("set", mPropertyName);
static String getMethodName(String prefix, String propertyName) {
        if (propertyName == null || propertyName.length() == 0) {
            // shouldn't get here
            return prefix;
        }
        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
        String theRest = propertyName.substring(1);
        return prefix + firstLetter + theRest;
    }

如果想获取set方法的方法名 第一个参数传入set,第二个参数就是我们的translationX, 它会截取这个字符串的第一个字符,然后设置成大写,也就是firstLetter等于大写T, 而theRest就是剩下的ranslationX,然后和prefix进行拼接 成 setTranslationX, 因此我们在写这个属性的时候搞不清首字母是大写还是小写,看到这里你就明白了 ,都可以哈。。

写到这里,这个分支是从上面的doAnimationFrame()方法里的startAnimation()方法进入顺下来的,下面再来看看另一个分支,就是在doAnimationFrame方法里startAnimation方法执行完后下面还有一个方法是animateBasedOnTime()方法,开始进入,看主要代码执行了animateValue(currentIterationFraction)方法,而这个方法也是一个坑,就是父类和子类都有这个方法,找代码的时候容易迷失方向。
看子类ObjectAnimator的animateValue方法

@CallSuper
    @Override
    void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up. Note: we allow null target if the
            /// target has never been set.
            cancel();
            return;
        }

        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }
    }

先是调用了父类的super.animateValue方法,然后又调用了PropertyValueHolder的setAnimatedValue方法。
先从父类的super.animateValue看起。

@CallSuper
    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);
            }
        }
    }

通过插值器Interpolator计算得到fraction,然后for循环调用PropertyValueHolder的calculateValue方法,计算得到的值赋值给PropertyValueHolder的变量mFloatAnimatedValue,就是动画执行到某一时刻的值。然后进入下面的for循环 mUpdateListeners.get(i).onAnimationUpdate(this);回调。这是父类(ValueAnimator)的animateValue方法执行的事情,再回到子类(ObjectAnimator)的animateValue方法,下面有一个for循环,调用了PropertyValueHolder的setAnimatedValue方法

void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString());
            }
        }
    }

mSetter.invoke(target, mTmpValueArray);
这个方法最后的调用才让我们的view执行了动画。
所以属性的动画的原理就是通过反射来改变view的属性从而让view动起来的。

结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值