美文网首页Android技术知识
属性动画源码分析

属性动画源码分析

作者: SharryChoo | 来源:发表于2018-02-25 00:34 被阅读55次

切入点

ObjectAnimator anim = ObjectAnimator.ofFloat(TextView(this), "scaleX", 0f, 1f)
anim.start()

从使用方式来分析, 属性动画有两个值得关注的点

  1. ObjectAnimator.ofFloat
  2. anim.start()

接下来一一分析

ObjectAnimator.ofFloat

    /**
     * objectAnimator.ofFloat 
     */
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        // 1. 构建了 ObjectAnimator 实例对象
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        // 2. 设置我们传入的数值
        anim.setFloatValues(values);
        return anim;
    }
    
    /**
     * ObjectAnimator.Constructor
     */
    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);// 给 mTarget(View) 赋值
        setPropertyName(propertyName);// 给 mPropertyName(String) 赋值
    }
    
    /**
     * ObjectAnimator.setFloatValues
     */
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                // 2.1 通过 PropertyValuesHolder.ofFloat(...) 获取 PropertyValuesHolder 对象
                // 2.2 调用 setValues 方法
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }
    
    /**
     * ValueAnimator.setFloatValues
     */
    public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;// 给成员变量赋值
        // 写入 mValuesMap 集合中
        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;
    }

好了分析了上面的源码, 我们可以看到 ObjectAnimator.ofFloat 主要做了如下

  1. 给成员变量 mTarget, mPropertyName 赋值
  2. 通过 PropertyValuesHolder.ofFloat 构建 PropertyValuesHolder 对象
  3. 将 PropertyValuesHolder 写入缓存 mValuesMap 中, 给 mValues, mValueMap 赋值

好, 接下来, 看看这个 PropertyValuesHolder.ofFloat 到底做了什么

    /**
     * PropertyValuesHolder.ofFloat
     */
    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }
    
    /**
     * PropertyValuesHolder.Constructor
     */
    public FloatPropertyValuesHolder(String propertyName, float... values) {
        super(propertyName);
        setFloatValues(values);
    }
    
    /**
     * PropertyValuesHolder.setFloatValues
     */
    public void setFloatValues(float... values) {
        super.setFloatValues(values);
        mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
    }
    
    /**
     * PropertyValuesHolder.setFloatValues
     */
    public void setFloatValues(float... values) {
        mValueType = float.class;
        // 重点来了, 给成员变量 mKeyframes 赋值
        mKeyframes = KeyframeSet.ofFloat(values);
    }
    
    /**
     * KeyframeSet.ofFloat
     */
    public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        // 将我们传入的 values 构建 Keyframe 的数组
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        // 若我们只传入一个值, 则 keyframes 给我们补上起始值 0f 
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        } 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]);
            }
        }
        // 通过我们的 keyframes 关键帧数组 来构建 FloatKeyframeSet 对象 
        return new FloatKeyframeSet(keyframes);
    }

通过 PropertyValuesHolder.ofFloat 分析可知

  1. PropertyValuesHolder 中有一个 mKeyframes(KeyframeSet) 成员变量
  2. mKeyframes(KeyframeSet) 中又保存了 FloatKeyframe[] 数组
  3. FloatKeyframe 中保存动画执行百分比 fraction 时对应 value 的值

好了, 至此 ObjectAnimator.ofFloat 做的事情就比较清楚了, 接下来看看 ObjectAnimator.start 方法是如何操作存储的这些数值的

ObjectAnimator.start

    /**
     * ObjectAnimator.start
     */
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        // 直接调用了父类的 start
        super.start();
    }
    
    /**
     * ValueAnimator.start
     */
    public void start() {
        start(false);
    }
    
    /**
     * ValueAnimator.start
     */
    private void start(boolean playBackwards) {
        // ...忽略相关代码
        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 = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // 1. 关注这个方法
            startAnimation();
            if (mSeekFraction == -1) {
                // 2. 立即执行
                setCurrentPlayTime(0);
            } else {
                // 延迟执行
                setCurrentFraction(mSeekFraction);
            }
        }
    }

可见 ObjectAnimator.start 最终会调用到 ValueAnimator 中的 start 方法, 我们先分析 startAnimation

    /**
     * ValueAnimator.startAnimation
     */
    private void startAnimation() {
        mAnimationEndRequested = false;
        // (重点) 初始化动画, 这个方法被 ObjectAnimator 复写了
        initAnimation();
        // 更改标记位置
        mRunning = true;
        if (mSeekFraction >= 0) {
            mOverallFraction = mSeekFraction;
        } else {
            mOverallFraction = 0f;
        }
        // 回调接口中的 onStart 方法
        if (mListeners != null) {
            notifyStartListeners();
        }
    }
    
    /**
     * ObjectAnimator.startAnimation
     */
    void initAnimation() {
        if (!mInitialized) {
            final Object target = getTarget();
            if (target != null) {
                final int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) {
                    // 1. 遍历 PropertyValuesHolder 数组, 分别调用 PropertyValuesHolder.setupSetterAndGetter
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            // 2. 回调父类的 initAnimation
            super.initAnimation();
        }
    }
    
    /**
     * PropertyValuesHolder.setupSetterAndGetter
     */
    void setupSetterAndGetter(Object target) {
        ...
        if (mProperty == null) {
            Class targetClass = target.getClass();
            // 这里看到了一个新的属性为 mSetter: Method 
            // 很明显了, mSetter 的作用是通过反射去调用 target 的目标方法
            // 例: mImageView.setScaleX(0f), setTranslationX() 等;
            if (mSetter == null) {
                // 1.1 拼接相关的 set 方法
                setupSetter(targetClass);
            }
            List<Keyframe> keyframes = mKeyframes.getKeyframes();
            int keyframeCount = keyframes == null ? 0 : keyframes.size();
            for (int i = 0; i < keyframeCount; i++) {
                Keyframe kf = keyframes.get(i);
                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                    if (mGetter == null) {
                        // 1.2 拼接相关的 get 方法
                        setupGetter(targetClass);
                        ...
                    }
                }
            }
        }
    }
    
    /** 
     * ValueAnimator.initAnimation
     */
    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }
    
    /**
     * ValueAnimator.init 
     */
    void init() {
        // 配置估值器相关
        if (mEvaluator == null) {
            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                    (mValueType == Float.class) ? sFloatEvaluator :
                    null;
        }
        if (mEvaluator != null) {
            mKeyframes.setEvaluator(mEvaluator);
        }
    }

可以看到 startAnimation() 这个方法主要做了一些初始化工作

  1. 根据 target 去拼接相关的 GetOrSet Method 方法, 用于后续反射调用更新 View 的属性值
  2. 配置了估值器相关
  3. 回调接口中的 onStart 方法

至此我们还是没有看到, 属性动画执行的过程, 但看到了 View 相关方法的拼接, 让我们感觉距离最后的执行又近了一步, 继续分析 setCurrentPlayTime(0)

    /**
     * ValueAnimator.init 
     */
    public void setCurrentPlayTime(long playTime) {
        float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
        setCurrentFraction(fraction);
    }
    
    /**
     * ValueAnimator.init 
     */
    public void setCurrentFraction(float fraction) {
        ... 
        final float currentIterationFraction = getCurrentIterationFraction(fraction); // 获取动画执行的百分比
        // 接下来看看 animateValue(被子类复写) 对这个动画百分比做了什么处理 
        animateValue(currentIterationFraction);
    }
    
    /**
     * ObjectAnimator.init 
     */
    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;
        // 遍历数组调用了 PropertyValuesHolder.setAnimatedValue 方法
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }
    }
    
    /**
     * PropertyValuesHolder.setAnimatedValue
     */
    void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                // 终于!!! 看到了反射调用方法, 最终实现了 View 的动态改变
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch(...) {
                ...
            }
        }
    }

通过源码分析可知 setCurrentPlayTime 才是属性动画真正的发起方法, 最终会调用 PropertyValuesHolder.setAnimatedValue 使用反射去调用相应的方法, 去改变 View 的动画

总结

通过上面的两大部分的源码分析, 对属性动画有了大体的认识, 其中还有很多细节没有去深究, 但可以从整体上了解了属性动画的工作流程

相关文章

  • 属性动画原理

    Android 属性动画详解与源码分析

  • 属性动画源码分析

    切入点 从使用方式来分析, 属性动画有两个值得关注的点 ObjectAnimator.ofFloat anim.s...

  • 属性动画源码分析

    分析版本api 24 首先我们要找一个入口,就从ObjectAnimator.ofInt(this, "width...

  • 属性动画源码分析

    * 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 属性动画有两个比较重要的动画执行类 其中 ...

  • Android属性动画源码分析

    分析源码最好的方式就是画时序图,搞清它的流程,时序图还有一个好处,就是看到某个地方,如果忘了前面的,可以直接看看图...

  • Android属性动画源码分析(二)

    在Android属性动画源码分析(一)我们说到接下来要进行的从示例1代码开始进行分析(基于API25的源码)。本篇...

  • 属性动画源码分析-ofFloat方法

    这里从最基本的使用分析源码,平时我们使用属性动画如下: 先来分析下ofFloat这个方法,这个方法是在Object...

  • Android属性动画源码分析(三)

    在上一篇文章中,我们对ObjectAnimator.ofFloat方法进行了分析,其中生成PropertyValu...

  • Android属性动画源码分析(一)

    按照官网上的介绍,属性动画框架是一个可以让任何“内容”动画化的框架,它可以让你通过在指定时间内修改对象的“属性值”...

  • Android属性动画源码分析(四)

      之前有点忙,有一阵没写了;好,今天开始属性动画的第四部分,前面三节把属性动画的初始化的过程基本分析了一遍,今天...

网友评论

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

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