属性动画源码探究

来源:互联网 发布:我的淘宝网首页登录 编辑:程序博客网 时间:2024/06/07 15:06

在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动起来的。

结束

原创粉丝点击