(二十二)Animator 源码分析

来源:互联网 发布:达尔朗 知乎 编辑:程序博客网 时间:2024/05/19 02:40

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

这是是根据 API 25 进行 ObjectAnimator 的使用代码分析。

    public void startAnimator(View view){        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);        objectAnimator.setDuration(500);        objectAnimator.start();    }

一、ofFloat

ObjectAnimator 的 ofFloat:

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

在这里,他先 new 了一个 ObjectAnimator,跟进去查看一下:调用了 setTarget 和 setPropertyName 方法。

  private ObjectAnimator(Object target, String propertyName) {        setTarget(target);        setPropertyName(propertyName);    }

很明显,setTarget 设置的是改属性动画的执行对象是谁,setPropertyName 设置要执行的是什么属性。

ObjectAnimator 的 setTarget:

    public void setTarget(@Nullable Object target) {        final Object oldTarget = getTarget();        if (oldTarget != target) {            if (isStarted()) {                cancel();            }            mTarget = target == null ? null : new WeakReference<Object>(target);            // New target should cause re-initialization prior to starting            mInitialized = false;        }    }

在 setTarget 中把要执行动画的对象存在成员变量 mTarget 中,这边采用的是弱引用,可以有效的避免内存泄漏。同样的,在 setPropertyName 中把执行动画的属性存在成员变量 mProperty 中。

继续 ofFloat 往下,调用了 ObjectAnimator 的 setFloatValues 方法。

ObjectAnimator 的 setFloatValues:

    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 是不会为空的,走 else,调用 ValueAnimator 的 setFloatValues 方法。

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

在 ValueAnimator 的 setFloatValues 中,使用了一个 PropertyValuesHolder 这个类,第一次调用 mValues 是空的,调用 PropertyValuesHolder.ofFloat(“”, values)。一直点下去,最终也跟 else 一样调用了 PropertyValuesHolder 的 setFloatValues 方法。

PropertyValuesHolder 的 setFloatValues:

    public void setFloatValues(float... values) {        //设置值类型        mValueType = float.class;        //设置关键帧        mKeyframes = KeyframeSet.ofFloat(values);    }

直接设置值类型 mValueType 为 float 类型,可以猜想,如果是 setIntValues ,那值类型 mValueType 设置为 int。

mKeyframes 是存放关键帧,关键帧在初始时候传入属性值变化的范围(下放代码 0f 和 200f 即关键帧)

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);

当传入多个的时候,关键帧也将变成多个。下方关键帧有 4 个,变化区间有三个,如果动画执行时间是 300ms,则每个区间的执行时间将均匀分配为 100ms。(为什么平均分配,后面看到再提)

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f, 500f, 400f);

KeyframeSet 的 ofFloat:

    public static KeyframeSet ofFloat(float... values) {        boolean badValue = false;        int numKeyframes = values.length;        //FloatKeyframe 是一个接口,在这 new 了一个 FloatKeyframe 数组        //关键帧只有一个的时候,默认加一个 0f        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) {                //(float) i / (numKeyframes - 1) 则是为了将时间均分                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);    }

在这边,判断关键帧的个数,当关键帧只有一个的时候,添加一个 0f 的关键帧,所以,下面两行代码效果是一致的:

ObjectAnimator.ofFloat(view,"translationX", 1f);ObjectAnimator.ofFloat(view,"translationX", 0f, 1f);

中间先不管,最中是 反悔了一个 new FloatKeyframeSet(keyframes),在这里直接调用 super(keyframes), FloatKeyframeSet 的父类是 KeyframeSet ,即又调回到 KeyframeSet 的构造方法。

KeyframeSet 的构造方法:

    public KeyframeSet(Keyframe... keyframes) {        mNumKeyframes = keyframes.length;        // immutable list        //保存所有帧        mKeyframes = Arrays.asList(keyframes);        //设置第一帧        mFirstKeyframe = keyframes[0];        //设置最后一帧        mLastKeyframe = keyframes[mNumKeyframes - 1];        mInterpolator = mLastKeyframe.getInterpolator();    }

顺便查看一下 KeyframeSet 的其他成员变量,可以发现,KeyframeSet 还保存有估值器和插值器。

总结: ObjectAnimator 的 ofFloat 方法主要做一些初始化操作,新建了一个 ObjectAnimator 对象,保存了要执行动画的对象和属性,并对关键帧进行了解析。

二、start

1.回调

接下来继续看一下 ObjectAnimator 的 start 方法。

ObjectAnimator 的 start:

    public void start() {        AnimationHandler.getInstance().autoCancelBasedOn(this);        if (DBG) {            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());            for (int i = 0; i < mValues.length; ++i) {                PropertyValuesHolder pvh = mValues[i];                Log.d(LOG_TAG, "   Values[" + i + "]: " +                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +                    pvh.mKeyframes.getValue(1));            }        }        super.start();    }

中间依稀处理忽略,最终调用了 super.start(),ObjectAnimator 的父类是 ObjectAnimator,即调用了 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,通过广播的机制由底层进行回调,每 16ms 进行一次回调。(再下去就比较底层了,涉及到垂直刷新,不继续了)

继续查看 AnimationHandler 的 addAnimationFrameCallback 方法。

AnimationHandler 的 addAnimationFrameCallback:

    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {        if (mAnimationCallbacks.size() == 0) {            //再分析下去比较复杂,总之广播接收到会调用 mFrameCallback。            getProvider().postFrameCallback(mFrameCallback);        }        //保存回调        if (!mAnimationCallbacks.contains(callback)) {            mAnimationCallbacks.add(callback);        }        if (delay > 0) {            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));        }    }

我们看一下变量 mFrameCallback,回调的时候调用了 doAnimationFrame 方法。getProvider().getFrameTime() 获取的当前动画执行的真正时间。

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

AnimationHandler 的 doAnimationFrame:

    private void doAnimationFrame(long frameTime) {        int size = mAnimationCallbacks.size();        long currentTime = SystemClock.uptimeMillis();        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();    }

这里的 callback 是由 ValueAnimator 中传递过来的 this,即 ValueAnimator 本身。ValueAnimator 的 doAnimationFrame 方法会调用 startAnimation 方法,跟 ObjectAnimator 的 start 一样。

2.反射方法

继续 ValueAnimator 的 start 方法,接下去直接调用 startAnimation() 启动动画,

ValueAnimator 的 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() 进行一些初始化操作,这边要注意一点的是,我们在这边分析的 ObjectAnimator 的源码,ObjectAnimator 有对 initAnimation 方法进行重写。

ObjectAnimator 的 initAnimation :

    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 是父类 ValueAnimator 的属性,是 PropertyValuesHolder                     mValues[i].setupSetterAndGetter(target);                }            }            super.initAnimation();        }    }

ObjectAnimator 的 initAnimation 调用了 PropertyValuesHolder 的 setupSetterAndGetter 方法。

PropertyValuesHolder 的 setupSetterAndGetter:

 void setupSetterAndGetter(Object target) {        ...            Class targetClass = target.getClass();            if (mSetter == null) {                setupSetter(targetClass);            }        ...    }

PropertyValuesHolder 的 setupSetter:

    void setupSetter(Class targetClass) {        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();        //传入 set,反射生成 set 方法。        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);    }

PropertyValuesHolder 的 setupSetterOrGetter:

   private Method setupSetterOrGetter(Class targetClass,            HashMap<Class, HashMap<String, Method>> propertyMapMap,            String prefix, Class valueType) {            ...                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);            ...        }        return setterOrGetter;    }

PropertyValuesHolder 的 getPropertyFunction:

 private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {        // TODO: faster implementation...        Method returnVal = null;        String methodName = getMethodName(prefix, mPropertyName);        Class args[] = null;        if (valueType == null) {            try {                returnVal = targetClass.getMethod(methodName, args);            } catch (NoSuchMethodException e) {                // Swallow the error, log it later            }        } else {            ...        }        return returnVal;    }

PropertyValuesHolder 的 getPropertyFunction 最终通过反射生产对应的 set 或 get 方法并返回。

在这边顺便提一下 getMethodName 方法。

PropertyValuesHolder 的 getMethodName:

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

getMethodName 方法对传入的方法名进行首字母修改为大写处理,所以在传属性的时候,首字母大小写是没有影响的。下方两行代码效果一样:

    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "TranslationX", 0f, 200f);

ObjectAnimator 的 initAnimation 在最后调用了 super.initAnimation(),即 ValueAnimator 的 initAnimation 方法。

ValueAnimator 的 initAnimation :

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

mValues[i].init() 调用了 PropertyValuesHolder 的 init,在这里进行了估值器的设置。

总结: 到这里,PropertyValuesHolder 的成员变量 mSetter 为对应属性的 set 方法。

3.方法的调用

再次回到 ValueAnimator 的 start 继续向下。

ValueAnimator 的 start :

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

ValueAnimator 的 setCurrentPlayTime :

    public void setCurrentPlayTime(long playTime) {        float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;        setCurrentFraction(fraction);    }

所以,ObjectAnimator 的 start 最终调用到了 setCurrentFraction 方法,传入的是动画执行的的百分比。在 setCurrentFraction 方法里面调用了 animateValue 方法。注意,ValueAnimator 也重写了该方法。

ObjectAnimator 的 animateValue :

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

ObjectAnimator 的 animateValue 调用了 super.animateValue(fraction), 在这里进行了插值器的使用。
最后调用了 mValues[i].setAnimatedValue(target),即 PropertyValuesHolder 的 setAnimatedValue 方法。
PropertyValuesHolder 的 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());            }        }    }

在这里进行前面反射的方法的调用,即我们设置的属性的 set 方法的调用,每次底层回调都会进行一次重新调用改属性的 set 方法进行设置,从而达到动画的效果。

阅读全文
0 0