美文网首页
属性动画源码解析

属性动画源码解析

作者: 猫KK | 来源:发表于2018-03-03 21:49 被阅读0次

    这里主要研究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 方法即可

    相关文章

      网友评论

          本文标题:属性动画源码解析

          本文链接:https://www.haomeiwen.com/subject/iyqxfftx.html