属性动画源码解析

2018-03-03  本文已影响0人  猫KK

这里主要研究ObjectAnimator 是如何改变控件的属性

用法

//第一个参数 需要改变的对象
//第二个参数 需要改变的属性的 set 方法,注意需要第一个参数拥有对应的set 方法
//第三、第四参数 改变的数值
ObjectAnimator animator = ObjectAnimator.ofFloat(new TextView(this), "scaleX", 0f, 1f);
        animator.setDuration(2000);
        animator.start();

我们来看ObjectAnimator.ofFloat 做了什么

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        //直接 new 了一个对象,设置值返回
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }

来看 ObjectAnimator 的构造方法

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

    @Override
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isStarted()) {
                cancel();
            }
            //这个方法主要将当前传进来的对象赋值给 mTarget ;根据我前面,当前的mTarget 为Textview
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

public void setPropertyName(@NonNull String propertyName) {
        // mValues could be null if this is being constructed piecemeal. Just record the
        // propertyName to be used later when setValues() is called if so.
       //根据注射可以知道当前mValues 为null
        if (mValues != null) {
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
            mValuesMap.put(propertyName, valuesHolder);
        }
        //主要将mPropertyName 赋值;根据前面 这个mPropertyName 为scaleX
        mPropertyName = propertyName;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

构造方法主要就是将 mTarget 和 mPropertyName 赋值在来看 anim.setFloatValues 方法

    @Override
    public void setFloatValues(float... values) {
        //此时mValues 为null
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            //刚开始,mProperty 也为 null
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                //所以走的是这里
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

    //setValues 只是将values 的值保存到 mValuesMap 中
    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 是怎么生成的

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

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

        @Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
           //一路进来会发现到这,将mFloatKeyframes 赋值,我们来看super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

走了一路最后发现调用的是父类的setFloatValues 方法

//PropertyValuesHolder 中

    public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

    //生成关键帧
    public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        //当我们传进来的values 只有一个时,会默认添加一个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 {
            //当values 不只有一个时,第0个为我们传进来的第0个
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                //循环 假设numKeyframes = 3 时
                //(float) i / (numKeyframes - 1) 的值就为 1/2 和 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);
    }

最后直接返回 FloatKeyframeSet ,这里就类似一个数据实体类,用来保存值;所以到这里主要做了一些保存值的操作,继续来看 start 方法

    @Override
    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));
            }
        }
        //直接调用父类的start
        super.start();
    }

我们发现它直接使用父类的start ,而ObjectAnimator 的父类就是ValueAnimator 进去

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

    private void start(boolean playBackwards) {
       //判断是否存在 Loop 对象
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        ......
        addAnimationCallback(0);
        .......
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            .....
            startAnimation();
            .........
        }
    }

addAnimationCallback 方法主要就是Animator 动画的数据流向,最终会回调到自身的animateValue 方法中,想了解的可以自行了解也可参照这篇博客

接下来继续看animateValue方法;

    //    ObjectAnimator 中

    @Override
    void animateValue(float fraction) {
        final Object target = getTarget();
        //判断 mTarget ;这个值在前面已经设置了
        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;
        }
        //调用父类的animateValue 主要做回调跟新操作,如开始和结束的监听
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            // 来看这里 
            mValues[i].setAnimatedValue(target);
        }
    }

还记得 mValues 值么,初始化的时候就赋值了

    //在    ObjectAnimator.ofFloat 的时候

    @Override
    public void setFloatValues(float... values) {
         setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
    }

     public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
    }

所以mValues 就是PropertyValuesHolder,所以调用的是PropertyValuesHolder.setAnimatedValue

        @Override
        void setAnimatedValue(Object target) {
            ......
            if (mSetter != null) {
                try {
                    mTmpValueArray[0] = mFloatAnimatedValue;
                    //通过反射调用 target 的set 方法;
                    mSetter.invoke(target, mTmpValueArray);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }

原来是通过反射来调用target 的set 方法,这个target 就是之前传进来的 TextView。那么这个 mSetter 是在哪里初始化的呢?来看startAnimation(); 方法

    private void startAnimation() {
        ....
        initAnimation();
        .....
    }

initAnimation(); 看名字就直到是初始化的方法,来看 ObjectAnimator 中的initAnimation

// ObjectAnimator 中

    @Override
    void initAnimation() {
        if (!mInitialized) {
            //判断target 是否为null 因为设置了所以不为 null
            if (target != null) {
                final int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    //来看这里
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            //调用父类的 initAnimation 
            super.initAnimation();
        }
    }

又是 mValues,通过上面可以直到这里调用的就是 PropertyValuesHolder.setupSetterAndGetter方法

    // PropertyValuesHolder 中
    
    void setupSetterAndGetter(Object target) {
        .....
        if (mProperty == null) {
            Class targetClass = target.getClass();
            if (mSetter == null) {
                //获取方法
                setupSetter(targetClass);
            }
        ......
    }

    void setupSetter(Class targetClass) {
        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
        //赋值mSetter
        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
    }

    private Method setupSetterOrGetter(Class targetClass,
            HashMap<Class, HashMap<String, Method>> propertyMapMap,
            String prefix, Class valueType) {
        Method setterOrGetter = null;
        synchronized(propertyMapMap) {
           .....
            if (!wasInMap) {
                //得到 setterOrGetter
                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
                if (propertyMap == null) {
                    //做缓存
                    propertyMap = new HashMap<String, Method>();
                    propertyMapMap.put(targetClass, propertyMap);
                }
                propertyMap.put(mPropertyName, setterOrGetter);
            }
        }
        return setterOrGetter;
    }

    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
        // TODO: faster implementation...
        Method returnVal = null;
        //通过拼接的得到 setXxx 的方法名
        String methodName = getMethodName(prefix, mPropertyName);
        Class args[] = null;
        if (valueType == null) {
            try {
                //当前 valueType 不为null 但是处理方式都是一样的,通过class 获取对应的方法
                //这属于反射内容,请自行了解
                returnVal = targetClass.getMethod(methodName, args);
            } catch (NoSuchMethodException e) {
                // Swallow the error, log it later
            }
        }
        .....
        return returnVal;
    }

到这里就知道 mSetter 的赋值时机;

总结

ObjectAnimator 动画其实就是通过获取对应类的 set 方法,然后反射调用,不断修改值来实现;而且对于第一个参数,可以是任意 Object 类,只要该类中设置又 set 方法即可

上一篇下一篇

猜你喜欢

热点阅读