阅读 168

Android源码解析(二)动画篇-- ObjectAnimator

Android源码解析-动画篇

注:

本篇文章将补充上篇文章中所遗留的知识点,本篇文章里的很多只是点,都依赖于上一次作者的源码分析,因此,如果读者尚未有知识积累,希望读者阅读 Android源码解析(一)动画篇-- Animator属性动画系统 后,再阅读此文章。

ObjectAnimator类:

本篇文章以ObjectAnimator类为主题,主要说明这个类在属性动画中的重要性。这里说的重要性并不代表这个类在整个属性动画系统的实现中有多么核心的地位 (因为整个属性动画的核心,还是依赖于ValueAnimatorAnimatorHandler所构建的消息和计算流程) ObjectAnimator的重要性体现在它使用的广泛性,我们将在很多地方看到他的运用场景。我们不妨先来看个View使用ObjectAnimator实现动画的栗子吧:

ObjectAnimator objectAnimator = ObjectAnimator
.ofFloat(view,View.TRANSLATION_X,0,500);//通过一个静态工厂方法构建ObjectAnimator对象
objectAnimator.setDuration(500).start();
复制代码

这里我们对比一下上一个我们通过View.animate()方式构建动画的代码:

view.animate().translationX(500).start();
复制代码

看起来貌似两者并没有什么两样,而且View.animate()方式似乎构造起来还更加方便一点。那么什么是ObjectAnimator呢?实际上,我在第一章的时候已经说过了,所谓属性动画系统实际上是分成两部分,一个是管理时序,一个是注入时序所对应的值,而你要注入的对象 Target 是谁呢? 可能是 View ,也可能是其他的对象,并不是所有的属性都要注入到View中的。这就是为什么 ObjectAnimatorObject 打头而不是以 View 打头。那么这样又存在另外的问题,既然我要注入的是一个可选择的对象,我又如何告诉 ObjectAnimator 我需要往这个对象的哪个属性去注入呢?我们先来看下 ObjectAnimator 的继承树:

ObjectAnimator继承树

可以看出,ObjectAnimator 还是继承于我们的 ValueAnimator 类型,根据继承特性, ObjectAnimator 就有了 时序控制属性计算注入 双重功能属性,那么我们上面提到的那个问题,我们要如何往一个任意对象注入我们所计算出来的属性值呢? 我们带着这个问题来看下我们上面用ObjectAnimator实现的动画代码。

ObjectAnimator例子:

ObjectAnimator objectAnimator = ObjectAnimator
.ofFloat(view,View.TRANSLATION_X,0,500);
//通过一个静态工厂方法构建ObjectAnimator对象
复制代码

第一行代码我们通过一个静态工厂方法构建一个ObjectAnimator对象,而这种方法模板像极了我们上一篇用ValueAnimator的构造方式:

public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setFloatValues(values);
        return anim;
    }
复制代码

这里, ObjectAnimator.ofFloat 会通过 ObjectAnimator 的双参构造器来构建一个 ObjectAnimator 对象,然后通过 setFloatValues 方法写入我们设置的过度区间。

private <T> ObjectAnimator(T target, Property<T, ?> property) {
        setTarget(target);
        setProperty(property);
}
复制代码

ObjectAnimator 的双参构造器其实就是记录了两个值: target 成员和 property 成员,target 成员用于记录我们需要注入的目标对象,保存在ObjectAnimator对象的一个 弱引用 中:

@Override
    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;
        }
    }
复制代码

因此,我们在使用 ObjectAnimator 的时候并不需要关心 target 的泄漏问题。而双参构造器另外一个非常重要的属性是一个 Property 类型的变量:

public void setProperty(@NonNull Property property) {
        if (mValues != null) {// mValues为null
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setProperty(property);
            mValuesMap.remove(oldName);
            mValuesMap.put(mPropertyName, valuesHolder);
        }
        if (mProperty != null) {
            mPropertyName = property.getName();
        }
        mProperty = property;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }
复制代码

由于 ObjectAnimator 刚开始构造,因此在构造器调用中的 mValues 属性为 null 。 我们回到上面的 ObjectAnimator.ofFloat 的静态方法,我们通过双参构造器构造完成以后,将通过 setFloatValues 设置一个区间量:

//code ObjectAnimator.java
@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) {//mProperty不为空
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } 
        ...
    }
复制代码

此时,由于 mProperty 变量不为空,因此调用:

//code ObjectAnimator.java
setValues(PropertyValuesHolder.ofFloat(Property, float ... values));
复制代码

Animator属性动画系统 中我们说了 PropertyValuesHolder.ofInt(String propertyName, int... values) 方法,本篇中又出现了一个新的构造方式 PropertyValuesHolder.ofFloat(Property, float ... values)

public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
    return new FloatPropertyValuesHolder(property, values);
}

public FloatPropertyValuesHolder(Property property, float... values) {
            super(property);
            setFloatValues(values);
            if (property instanceof  FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
        }
复制代码

那么什么是 Property 呢?我们看下类注释:

/**
 * A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
 * in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
 * methods can be implemented in terms of the private fields of the host object, or via "setter" and
 * "getter" methods or by some other mechanism, as appropriate.
 *
 * @param <T> The class on which the property is declared.
 * @param <V> The type that this property represents.
 */
复制代码

大致意思就像它的名字一样,是一个属性的抽象,不过这个属性可以通过 setget 的 方式来获取,这么说或许有点抽象,我们不妨看下我们传入的 View.TRANSLATION_X 是如何实现的:

public static final Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
        @Override
        public void setValue(View object, float value) {
            object.setTranslationX(value);
        }

                @Override
        public Float get(View object) {
            return object.getTranslationX();
        }
    };
复制代码

View.TRANSLATION_X 实现了 setValueget 方法,它就像一个代理类,帮你获取 "以 ViewTarget 目标对象" 的属性和设置这个目标对象的属性值。

属性值的注入:

基于上面我们的知识储备,我们来看下 ObjectAnimator.start() 方法:

 @Override
    public void start() {
        ...
        super.start();//ValueAniamtor.start
    }
复制代码

可以看出, ObjectAnimator.start() 实际上,最终调用的还是它的父类 ValueAniamtorstart 方法。回顾一下我们上一次的流程图:

ValueAnimator.start调用流程图

当我们通过系统 VSYNC 信号收到一条绘制指令的时候,最终将会回调 ValueAnimator.doAnimationFrame() 方法。根据: Android源码解析(一)动画篇-- Animator属性动画系统 我们可以知道, ValueAnimator.doAnimationFrame 将产生以下的调用链:

doAnimationFrame 
-> animateBasedOnTime 
-> animateValue 
-> notifyUpdateListeners
复制代码

而在开篇中,我们介绍了 View.animate() 的实现原理,实际上,是采用注册监听器的方式来完成。而此时 ObjectAnimator 子类并不是采用这种方式,在 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);
        }
    }
复制代码

在这里, ObjectAnimator 将会调用 super.animateValue(fraction); 来通知注册回调,然后通过遍历 mValues 数组中的值去修改或注入 target 对象中的属性。那么 mValues 是什么呢? mValues 就是在父类 ValueAnimator 中的 PropertyValuesHolder[] mValues; 只不过这个 PropertyValuesHolder 对象数组多保存了一个 Property 属性

void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
    }
复制代码

由于我们刚开始传入的 PropertyView.TRANSLATION_X 对象,所以将调用 View.TRANSLATION_X.set 方法,又由于 View.TRANSLATION_X 继承于 FloatProperty 类型, FloatProperty 类复写了 set 方法,内部将调用到 setValue(View,Float) 方法:

//code FloatProperty:
    @Override
    final public void set(T object, Float value) {
        setValue(object, value);
    }
复制代码

因此, ObjectAnimatorsetAnimatedValue 方法将调用到 View.TRANSLATION_X.setValue方法,从而就完成了属性的注入:

public void setValue(View object, float value) {
       object.setTranslationX(value);
 }
复制代码
评论