文章

属性动画原理

属性动画原理

属性动画原理

相关类

ObjectAnimator 和 ValueAnimator 及区别

  1. ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作
  2. ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性 (反射调用对象的 setter 方法,还是需要手动 invalidate() 等操作的) 从而实现动画;是 直接 对对象属性进行操作;

Property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class Property<T, V> {
    private final String mName;
    private final Class<V> mType;
    public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
        return new ReflectiveProperty<T, V>(hostType, valueType, name);
    }
    public Property(Class<V> type, String name) {
        mName = name;
        mType = type;
    }
    public boolean isReadOnly() {
        return false;
    }
    public void set(T object, V value) {
        throw new UnsupportedOperationException("Property " + getName() +" is read-only");
    }
    public abstract V get(T object);
    public String getName() {
        return mName;
    }
    public Class<V> getType() {
        return mType;
    }
}
  1. set 方法的参数和 get 方法的返回值同一个 T 类型
  2. 在属性动画中,set 用于更新动画需要的值,动画更新(如调用 invalidate())
  3. 在属性动画中,get 用于使用者获取没有提供初始值
  4. 提供可包装三方 view 未能正确的实现 setter/getter 方法来实现 ObjectAnimator 属性动画

PropertyValuesHolder setter/getter; 计算值; 更新值

PropertyValuesHolder 作用

  1. 持有 Property 用于获取动画初始化值 get, 更新动画值 set;持有 mPropertyName 通过反射获取 getter/setter 方法,获取初始值和更新动画值
  2. 计算动画过程中的值(calculateValue),根据 fraction,具体通过 Keyframes.getValue()(int/float 是 IntKeyframesSet/FloatKeyFramesSet)
  3. 更新动画过程中的值(setAnimatedValue),调用 Property 的 set 或者 target.propertyName 的 setter 方法更新值
  4. 所有的属性动画的 value 都转化成 PropertyValuesHolder 来操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
public class PropertyValuesHolder {
    // int类型默认估值器
    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
    // float类型默认估值器
    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
    
    String mPropertyName; // ofXXX传递进来的
    protected Property mProperty; // ofXXX传递进来的
    Method mSetter = null;
    private Method mGetter = null;
    Class mValueType;
    Keyframes mKeyframes = null;
    private TypeEvaluator mEvaluator;
    private Object mAnimatedValue;
    private TypeConverter mConverter;

    private PropertyValuesHolder(String propertyName) {
        mPropertyName = propertyName;
    }
    private PropertyValuesHolder(Property property) {
        mProperty = property;
        if (property != null) {
            mPropertyName = property.getName();
        }
    }
    public static PropertyValuesHolder ofInt(String propertyName, int... values) {
        return new IntPropertyValuesHolder(propertyName, values);
    }
    public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
        return new IntPropertyValuesHolder(property, values);
    }
    
    // 反射获取getXXX方法(mPropertyName的首字母大写前加get,如foo,getFoo);如果有mConverter那么从mConverter中获取,否则从mValueType中获取
    private void setupGetter(Class targetClass) {
        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
    }
    
    // 反射获取setXXX方法(mPropertyName的首字母大写前加set,如foo,setFoo);如果有mConverter那么从mConverter中获取,否则从mValueType中获取
    void setupSetter(Class targetClass) {
        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
    }
    
    // 在ValueAnimator.startAnimation调用 → ObjectAnimator重写initAnimation调用(在ValueAnimator的initAnimation不会调用setupSetterAndGetter)
    void setupSetterAndGetter(Object target) {
        if (mProperty != null) { // mProperty不为null
            try {
                Object testValue = null;
                List<Keyframe> keyframes = mKeyframes.getKeyframes(); // mKeyframes在ofXXX已经赋值了
                int keyframeCount = keyframes == null ? 0 : keyframes.size();
                for (int i = 0; i < keyframeCount; i++) {
                    Keyframe kf = keyframes.get(i);
                    if (!kf.hasValue() || kf.valueWasSetOnStart()) { // 未设置初始值(ofXXX只提供了一个值,Keyframe只有一个参数的构造方法) 或 设置了从gettter中获取值(这里是Propery.get)
                        if (testValue == null) {
                            testValue = convertBack(mProperty.get(target));
                        }
                        kf.setValue(testValue);
                        kf.setValueWasSetOnStart(true);
                    }
                }
                return;
            } catch (ClassCastException e) { // Property和target类型对应不上
                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                        ") on target object " + target + ". Trying reflection instead");
                mProperty = null;
            }
        }
        // 没有写else是因为mProperty可能和target类型对应不上
        if (mProperty == null) { // mProperty为null或mProperty可能和target类型对应不上被置为null了
            Class targetClass = target.getClass();
            if (mSetter == null) {
                setupSetter(targetClass);  // 注册setter
            }
            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()) { // 未设置初始值(ofXXX只提供了一个值,Keyframe只有一个参数的构造方法) 或 设置了从gettter中获取值
                    if (mGetter == null) {
                        setupGetter(targetClass);
                        if (mGetter == null) {
                            // Already logged the error - just return to avoid NPE
                            return;
                        }
                    }
                    try {
                        Object value = convertBack(mGetter.invoke(target));
                        kf.setValue(value);
                        kf.setValueWasSetOnStart(true);
                    } catch (InvocationTargetException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    } catch (IllegalAccessException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    }
                }
            }
        }
    }
    
    // 根据当前动画进度fraction计算具体的动画值;在ValueAnimator#animateValue调用
    void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction); // 调用Keyframes#getValue计算出具体的动画值
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value); // 如果设置了mConverter,转换下
    }
    
    // 更新值,在ObjectAnimator#animateValue中调用(animateValue是在ValueAnimator接受到时钟脉冲信号回调调用);mProperty和mSetter一般是互斥的,2种逻辑只会选择一种
    void setAnimatedValue(Object target) {
        if (mProperty != null) { // mProperty不为null,调用set更新值
            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());
            }
        }
    }
    
    
    // ValueAnimator.initAnimation调用;自动处理Integer和Float的估值器,int和float不会走到这里,分别走IntKeyframeSet和FloatKeyframeSet
    void init() {
        if (mEvaluator == null) {
            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
        }
        if (mEvaluator != null) {
            mKeyframes.setEvaluator(mEvaluator);
        }
    }
}
  1. init ValueAnimator.initAnimation 调用;自动处理 Integer 和 Float 的估值器,int 和 float 不会走到这里,分别走 IntKeyframeSet 和 FloatKeyframeSet
  2. setupSetterAndGetter 通过反射获取属性的 get set 方法或 Property 的 set/get 设置初始值(当然 一些 PropertyValuesHolder 的子类重载使用了 jni 方法获得反射方法。其实都是目的都是一样。)
  3. 更新值,先通过 calculateValue 计算出动画需要的值,再通过 setAnimatedValue 调用 Property#set 或反射 target 的 setter 更新值

Keyframe/Keyframes/KeyframeSet

关键帧的作用:

  1. 关键帧 keyframe 用于保存动画的每一个关键数据帧,个数由传入的 value 个数决定,系数 fraction 由 value 传入顺序决定。
  2. 保存每个关键帧的 fraction
  3. 通过动画类传入的 fraction,利用估值器计算出 真正的属性值。

Keyframe 关键帧

记录关键帧数据,他默认有三个实现类 IntKeyframeFloatKeyframeObjectKeyframe,当然我们也能继承 Keyframe 实现自己的关键帧类型。不过大部分情况,提供的这三种方式已经够用了。

Keyframe 有三个重要的属性值:

  1. mHasValue 用于记录关键帧是否初始化了值,以 子类 IntKeyframe 为例,它有两个构造方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Keyframe#IntKeyframe Android29
static class IntKeyframe extends Keyframe {
    /**
     * The value of the animation at the time mFraction.
     */
    int mValue;
    
    IntKeyframe(float fraction, int value) { // #1
        mFraction = fraction;
        mValue = value;
        mValueType = int.class;
        mHasValue = true;
    }
    
    IntKeyframe(float fraction) { // #2
        mFraction = fraction;
        mValueType = int.class;
    }
}

第一个构造方法 (1) 初始化了 mValue ,同时 mHasValue 会标记 为 true;第二个构造方法 (2) 只初始化了 mFraction,并未给 mValue 赋值,默认 mHasValue 为 false;假如我们使用了 mHasValue 为 false 的关键帧。那么在动画初始化时会调用 PropertyValuesHolder.setupSetterAndGetter 给每一个关键帧赋初始值。

  1. mFraction 记录该关键帧所有关键帧中的位置,float 类型,值范围 0 - 1
  2. mValue 记录 value 值,当然不同类型的 Keyframe value 值类型也不同,所以 mValue 由子类实现

Keyframes 一堆 Keyframe 组成的集合

它的实现类有 KeyframeSetPathKeyframes,它的子接口有 IntKeyframesFloatKeyframes

1
2
3
4
5
6
7
8
9
// Keyframes Android29
public interface Keyframes {
    // 估值器,计算动画值时用;不是IntKeyframes和FloatKeyframes需要设置估值器
    void setEvaluator(TypeEvaluator evaluator);
    Class getType();
    // 获取动画值;随着动画的完成度,interpolator和evaluator计算出出来的真正的动画值
    Object getValue(float fraction);
    List<Keyframe> getKeyframes();
}
KeyframeSet

两个子类 IntKeyframeSet、FloatKeyframeSet,分别实现了对应的 IntKeyframes 和 FloatKeyframes 接口。

ofInt 用的是 IntKeyframeSet,float 用的是 FloatKeyframeSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// KeyframeSet Android29
public class KeyframeSet implements Keyframes {
    int mNumKeyframes; // 关键帧数量

    Keyframe mFirstKeyframe; // 第一帧
    Keyframe mLastKeyframe; // 最后一帧
    TimeInterpolator mInterpolator; // only used in the 2-keyframe case;插值器,只有数量2的关键帧用这个TimeInterpolator
    List<Keyframe> mKeyframes; // only used when there are not 2 keyframes,不是数量2关键帧
    TypeEvaluator mEvaluator; // 估值器
    
    public KeyframeSet(Keyframe... keyframes) {
        mNumKeyframes = keyframes.length;
        // immutable list
        mKeyframes = Arrays.asList(keyframes);
        mFirstKeyframe = keyframes[0];
        mLastKeyframe = keyframes[mNumKeyframes - 1];
        mInterpolator = mLastKeyframe.getInterpolator();
    }
    
    public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }
    // ofFloat
    // ofKeyframe
    // ofObject
    // ofPath
    
    public Object getValue(float fraction) {
        // Special-case optimization for the common case of only two keyframes
        if (mNumKeyframes == 2) { // 关键帧数量为2 
            if (mInterpolator != null) {
                fraction = mInterpolator.getInterpolation(fraction); // 插值器计算出fraction
            }
            return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
                    mLastKeyframe.getValue()); // 估值器算出真正的动画值
        }
        if (fraction <= 0f) {
            final Keyframe nextKeyframe = mKeyframes.get(1);
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            if (interpolator != null) {
                fraction = interpolator.getInterpolation(fraction);
            }
            final float prevFraction = mFirstKeyframe.getFraction();
            float intervalFraction = (fraction - prevFraction) /
                (nextKeyframe.getFraction() - prevFraction);
            return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
                    nextKeyframe.getValue());
        } else if (fraction >= 1f) {
            final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
            final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
            if (interpolator != null) {
                fraction = interpolator.getInterpolation(fraction);
            }
            final float prevFraction = prevKeyframe.getFraction();
            float intervalFraction = (fraction - prevFraction) /
                (mLastKeyframe.getFraction() - prevFraction);
            return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                    mLastKeyframe.getValue());
        }
        Keyframe prevKeyframe = mFirstKeyframe;
        for (int i = 1; i < mNumKeyframes; ++i) {
            Keyframe nextKeyframe = mKeyframes.get(i);
            if (fraction < nextKeyframe.getFraction()) {
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                final float prevFraction = prevKeyframe.getFraction();
                float intervalFraction = (fraction - prevFraction) /
                    (nextKeyframe.getFraction() - prevFraction);
                // Apply interpolator on the proportional duration.
                if (interpolator != null) {
                    intervalFraction = interpolator.getInterpolation(intervalFraction);
                }
                return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                        nextKeyframe.getValue());
            }
            prevKeyframe = nextKeyframe;
        }
        // shouldn't reach here
        return mLastKeyframe.getValue();
    }
}

Keyframes 最核心的方法是 getValue 方法。

  1. 仅有两个关键帧时,只有在这种情况下才使用 mInterpolator- 插值器。通过插值器 得到修正后的系数 fraction,然后拿这个系数,通过估值器 mEvaluator 计算出属性值
  2. if (fraction <= 0f) 和 if (fraction >= 1f) 这两个分支是特殊情况,出现在动画第一帧和最后一帧的情况
  3. 正常动画中的分支,通过 fraction 查找符合 fraction < nextKeyframe.getFraction() 的第一帧,然后计算 fraction 在前一帧到当前帧范围内的位置。

例如查找到符合条件的当前帧 fraction =0.5; 前一帧 fraction = 0.2, 动画当前的 fraction =0.3,那么新的 intervalFraction = 1/3f。然后拿着这个值给估值器计算属性值。

TimeInterpolator 插值器

插值器,定义了动画的变化速率 (消逝到篡改的一个映射),允许动画有非线性的变化,如加速、减速

1
2
3
4
5
6
7
// TimeInterpolator Android29
public interface TimeInterpolator {
    // elapsed fraction(消逝的部分)和 interpolated fraction(篡改的部分)的一个映射
    float getInterpolation(float input); 
    // 参数input,[0~1],0表示开始,1表示结束
    // 返回值,可能大于1表示overshoot,小于0表示undershoot
}

TypeEvaluator 估值器

估值器,动画变化过程中根据当前进度 fraction 计算出的做动画的实际值

1
2
3
4
5
6
7
8
public interface TypeEvaluator<T> { 
   public T evaluate(float fraction, T startValue, T endValue);
   // 参数fraction 当前动画进度
   // 参数startValue 开始值
   // 参数endValue 结束值
   
   // 返回值 简单的计算公式:result = startValue + fraction * (endValue - startValue)
}

属性动画原理 以 ofInt 举例

动画的创建

常用的属性动画创建方法

1
2
3
4
5
ObjectAnimator.ofInt()
ObjectAnimator.ofFloat()
ObjectAnimator.ofArgb()
ObjectAnimator.ofObject()
ObjectAnimator.ofPropertyValuesHolder()

以一个常用的方法 ObjectAnimator.ofInt() 为切入点层层深入解析属性动画的创建过程。

早期版本的属性动画 ofInt 方法有两个实现,到 Android O 为止已经有 4 个实现了,主要就是增加了 Path 参数:

1
2
3
4
5
6
7
8
9
10
11
// ObjectAnimator Android29
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName); // #1
        anim.setIntValues(values); // #2
        return anim;
    }
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, property); // #1
        anim.setIntValues(values);  // #2
        return anim;
    }

ofInt 方法的第一行 (propertyName1) 均是创建 ObjectAnimator 对象。但分别使用了两种构造方式 propertyName 和 Property。

ObjectAnimator 构造

1
2
3
4
5
6
7
8
9
10
// ObjectAnimator Android29
public ObjectAnimator() {}
private ObjectAnimator(Object target, String propertyName) {
    setTarget(target); #propertyName#1
    setPropertyName(propertyName); #propertyName#2
}
private <T> ObjectAnimator(T target, Property<T, ?> property) {
    setTarget(target); #Property#1
    setProperty(property); #Property#2
}

另外两个构造方法第一行代码都是 setTaret:

setTaret(Object)
1
2
3
4
5
6
7
8
9
10
11
12
13
private WeakReference<Object> mTarget;
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(target);

mTarget 是记录需要做动画的对象的虚引用,方便以后调用该对象 getter/setter;如果 mTarget 不存在时,在 animateValue 会 cancel 当前动画。

setPropertyName/setProperty

propertyName2,setPropertyName(String)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.
    if (mValues != null) {
        PropertyValuesHolder valuesHolder = mValues[0];
        String oldName = valuesHolder.getPropertyName();
        valuesHolder.setPropertyName(propertyName);
        mValuesMap.remove(oldName);
        mValuesMap.put(propertyName, valuesHolder);
    }
    mPropertyName = propertyName;
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

mValues 肯定为 null,不会走 if 分支,赋值 mPropertyName;如果 mValues 不为空的情况下,会更新 mValues 的第一个元素的属性值,并更新对应的 map 集合

Property2,setProperty(Property)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void setProperty(@NonNull Property property) {
    // mValues could be null if this is being constructed piecemeal. Just record the
    // propertyName to be used later when setValues() is called if so.
    if (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;
}

同 setPropertyName(String),mPropertyName 从 property.getName 获取,赋值 mProperty

现在看 anim.setIntValues(values);(#2):

1
2
3
4
5
6
7
8
9
10
11
12
// ObjectAnimator Android29
public void setIntValues(int... values) {
    if (mValues == null || mValues.length == 0) {
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofInt(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
        }
    } else {
        super.setIntValues(values);
    }
}

第一次调用 mValues 为 null 走入 if 流程,假如我们调用的 propertyName 参数的方法,会执行 setValues(PropertyValuesHolder.ofInt(mPropertyName, values));,反之执行 setValues(PropertyValuesHolder.ofInt(mProperty, values));

我们先看看 PropertyValuesHolder.ofInt(mProperty/propertyName, values):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// PropertyValuesHolder Android29
public class PropertyValuesHolder {
    private PropertyValuesHolder(String propertyName) {
        mPropertyName = propertyName;
    }
    private PropertyValuesHolder(Property property) {
        mProperty = property;
        if (property != null) {
            mPropertyName = property.getName();
        }
    }
    public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
        return new IntPropertyValuesHolder(property, values);
    }
    public static PropertyValuesHolder ofInt(String propertyName, int... values) {
        return new IntPropertyValuesHolder(propertyName, values);
    }
    public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframes = KeyframeSet.ofInt(values);
    }
}
static class IntPropertyValuesHolder extends PropertyValuesHolder {
    public IntPropertyValuesHolder(String propertyName, int... values) {
        super(propertyName);
        setIntValues(values);
    }
    public IntPropertyValuesHolder(Property property, int... values) {
        super(property);
        setIntValues(values);
        if (property instanceof  IntProperty) {
            mIntProperty = (IntProperty) mProperty;
        }
    }
    @Override
    public void setIntValues(int... values) {
        super.setIntValues(values);
        mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
    }
}

PropertyValuesHolder#ofInt,创建一个 IntPropertyValuesHolder,保存 propertyName/Property,调用 PropertyValuesHolder#setIntValues,生成关键帧 (keyframe)。

ofInt

现在回到 ObjectAnimator.ofInt,看看 setIntValues()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    anim.setIntValues(values);
    return anim;
}
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);
    }
    mInitialized = false;
}
public void setIntValues(int... values) {
    if (mValues == null || mValues.length == 0) {
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofInt(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
        }
    } else {
        super.setIntValues(values);
    }
}

可以看到用 values 用单个 PropertyValuesHolder 包装了下,赋值给了 mValues

在上面 PropertyValuesHolder 知道 PropertyValuesHolder.ofInt 会生成关键帧 keyframes 信息

动画启动 start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// ObjectAnimator Android29
public void start() {
    AnimationHandler.getInstance().autoCancelBasedOn(this);
    super.start();
}

// ValueAnimator Android29
public void start() {
    start(false);
}
private void start(boolean playBackwards) {
    mReversing = playBackwards;
    // 动画的状态
    mStarted = true;
    mPaused = false;
    mRunning = false;
    // ... 
    addAnimationCallback(0); 
    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        // 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) { // mSeekFraction为负数,没有seek,从0开始
            // 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); // 最终也是调用的setCurrentFraction
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}
public void setCurrentFraction(float fraction) { // 
    initAnimation();
    fraction = clampFraction(fraction);
    // ...
    mOverallFraction = fraction;
    final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
    animateValue(currentIterationFraction);
}
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction); // 
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this); // 通知AnimatorUpdateListener值更新了
        }
    }
}

如果是 ObjectAnimator,AnimationHandler.getInstance().autoCancelBasedOn(this) 会 cancel 相同 Target 和相同属性的动画,AnimationHandler 实例在线程局部单例。autoCancelBasedOn(this) 会遍历 AnimationHandler 实例持有的所有未完成的 ValueAnimator 实例,cancel 掉符合条件的动画。

紧接着 super.start() 调用了 ValueAnimator.start()

  • fraction 是什么?
    动画完成度,如果没有设置重复,就是 [01],如果设置了重复值为 [0repeatCount+1]
1
2
3
4
5
6
7
8
private float clampFraction(float fraction) {
    if (fraction < 0) {
        fraction = 0;
    } else if (mRepeatCount != INFINITE) {
        fraction = Math.min(fraction, mRepeatCount + 1);
    }
    return fraction;
}
  • 在 animateValue,通过插值器 TimeInterpolator 根据当前 fraction,计算出一个真正的 fraction
  • 可以看到,调用了 start 了,通过 addAnimationCallback 添加了一个什么 Calllback,稍后看;通知 AnimatorUpdateListener 值更新了

现在看 addAnimationCallback(this)

1
2
3
4
5
6
7
8
9
10
// ValueAnimator Android29
private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay); // 如果dalay大于0,callback 会在delay 时间后的下一个frame获得回调
}
public AnimationHandler getAnimationHandler() {
    return AnimationHandler.getInstance();
}

通过 AnimationHandler 的 addAnimationFrameCallback 添加了个回调,AnimationHandler 是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// AnimationHandler Android29
public class AnimationHandler {
    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime()); // 处理当前帧
            if (mAnimationCallbacks.size() > 0) { // 如果当前当前未结束,mAnimationCallbacks>0的,继续注册下一帧的callback。为什么还要注册一次呢?之前不是注册过一次了吗?难道Choreographer把这个callback 释放了:是在Choreographer#recycleCallbackLocked(callbacks);来回收整个链表中的节点记录。
                getProvider().postFrameCallback(this);
            }
        }
    };
    public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>(); 
    public static AnimationHandler getInstance() { // 线程单例,即主线程都共享该实例,所有的属性动画都通过这个来处理
        if (sAnimatorHandler.get() == null) {
            sAnimatorHandler.set(new AnimationHandler());
        }
        return sAnimatorHandler.get();
    }
    
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }
    
    private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }
}

如果是首次 getProvider().postFrameCallback(mFrameCallback);,getProvider() 是啥?

1
2
3
4
5
6
7
// AnimatorHandler Android29
private AnimationFrameCallbackProvider getProvider() {
    if (mProvider == null) {
        mProvider = new MyFrameCallbackProvider();
    }
    return mProvider;
}

创建了一个 MyFrameCallbackProvider 实例,MyFrameCallbackProvider 继承 AnimationFrameCallbackProvider

1
2
3
4
5
6
7
8
// AnimatorHandler Android29
public interface AnimationFrameCallbackProvider {
    void postFrameCallback(Choreographer.FrameCallback callback);
    void postCommitCallback(Runnable runnable);
    long getFrameTime();
    long getFrameDelay();
    void setFrameDelay(long delay);
}

AnimationFrameCallbackProvider 接口定义了一些回调接口,按照注释说明主要作用是 提高 ValueAnimator 的可测性,通过这个接口隔离,我们可以自定义 定时脉冲,而不用使用系统默认的 Choreographer,这样我们可以在测试中使用任意的时间间隔的定时脉冲.既然可以方便测试,那肯定有 API 来更改 Provider:那就是 setProvider。

1
2
3
4
5
6
7
8
// AnimatorHandler Android29
public void setProvider(AnimationFrameCallbackProvider provider) {
    if (provider == null) {
       mProvider = new MyFrameCallbackProvider();
    } else {
       mProvider = provider;
    }
}

ValueAnimator 提供了一个 setProvider 通过自定义的 Provider 提供我们想要的任意时间间隔的回调,来更新动画。

最终走的是 MyFrameCallbackProviderpostFrameCallback(),现在我们看看 MyFrameCallbackProvider 是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// MyFrameCallbackProvider Android29
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

    final Choreographer mChoreographer = Choreographer.getInstance();

    @Override
    public void postFrameCallback(Choreographer.FrameCallback callback) {
        mChoreographer.postFrameCallback(callback);
    }

    @Override
    public void postCommitCallback(Runnable runnable) {
        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
    }

    @Override
    public long getFrameTime() {
        return mChoreographer.getFrameTime();
    }

    @Override
    public long getFrameDelay() {
        return Choreographer.getFrameDelay();
    }

    @Override
    public void setFrameDelay(long delay) {
        Choreographer.setFrameDelay(delay);
    }
}

MyFrameCallbackProvider.postFrameCallback() 调用了 Choreographer.postFrameCallback(),使用 vsync(垂直同步)来协调 View 的绘制和动画的执行间隔。最终回调到 AnimationFrameCallbackboolean doAnimationFrame(long frameTime); 方法,frameTime 的时间是 SystemClock#uptimeMillis(),返回 true 动画结束。

系统默认使用的 Choreographer 做定时脉冲来协调 frame 的更新;Choreographer 实例也是线程局部单例的。

现在我们又回到了 ValueAnimator 的 doAnimationFrame 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ValueAnimator Android29
// 处理一帧的动画,frameTime,当前帧的SystemClock#uptimeMillis()时间
public final void doAnimationFrame(long frameTime) {
   AnimationHandler handler = AnimationHandler.getInstance();
   if (mLastFrameTime == 0) {
       // First frame
       //如果是动画的第一次回调,注册调整到下一个 frame 窗口 再执行。
       handler.addOneShotCommitCallback(this);
       if (mStartDelay > 0) {
           startAnimation();
       }
       if (mSeekFraction < 0) {
           mStartTime = frameTime;
       } else {
           long seekTime = (long) (getScaledDuration() * mSeekFraction);
           mStartTime = frameTime - seekTime;
           mSeekFraction = -1;
       }
       mStartTimeCommitted = false; // allow start time to be compensated for jank
    }
    mLastFrameTime = frameTime;
    if (mPaused) {
        mPauseTime = frameTime;
        handler.removeCallback(this);
        return;
    } else if (mResumed) {
        mResumed = false;
        if (mPauseTime > 0) {
            // Offset by the duration that the animation was paused
            mStartTime += (frameTime - mPauseTime);
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        handler.addOneShotCommitCallback(this);
   }
   // ... comments
   final long currentTime = Math.max(frameTime, mStartTime);
   boolean finished = animateBasedOnTime(currentTime); // 返回值finished标记是否动画已经执行完毕。如果最后一个关键帧(Keyframe)执行完毕,这里返回true,会执行endAnimation()做一些状态位复位和动画结束回调等等。
   if (finished) {
       endAnimation();
   }
}

frameTime 是当前 frame 时间,调用 animateBasedOnTime(currentTime)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
boolean animateBasedOnTime(long currentTime) {
   boolean done = false;
   if (mRunning) {
       final long scaledDuration = getScaledDuration(); // 默认不scale,调用overrideDurationScale(float durationScale)进行设置scale
       final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
       final float lastFraction = mOverallFraction;
       final boolean newIteration = (int) fraction > (int) lastFraction;
       final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
       if (scaledDuration == 0) {
           // 0 duration animator, ignore the repeat count and skip to the end
           done = true;
       } else if (newIteration && !lastIterationFinished) {
          // Time to repeat
          if (mListeners != null) {
              int numListeners = mListeners.size();
              for (int i = 0; i < numListeners; ++i) {
                   mListeners.get(i).onAnimationRepeat(this);
              }
           }
       } else if (lastIterationFinished) {
           done = true;
       }
       mOverallFraction = clampFraction(fraction);
       float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
       animateValue(currentIterationFraction);
   }
   return done;
}
  1. currentTime 是 Choreographer 发出的计时脉冲时间,纳秒计时。
  2. fraction 根据 currentTime 计算 fraction(currentTime-mStartTime/duration) 系数,即动画时间流逝比。
  3. 然后执行 ValueAnimator#animateValue(currentIterationFraction) 计算动画的在当前时间比例下属性动画的值;如果是 ObjectAnimator 还会降属性值设置该 Target。animateValue 方法被 ObjectAnimator 重载了。

ValueAnimator#animateValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ValueAnimator Android29
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}
  1. 参数 fraction 这个时间流逝比系数是线性的。通过 mInterpolator.getInterpolation() 计算出我们想要的 fraction;
  2. 从前面可以知道 mValues 是 PropertyValuesHolder,遍历所有的 PropertyValuesHolder.calculateValue(fraction) 使用这个系数 fraction 计算出 mAnimatedValue,从前面的 PropertyValuesHolder 分析可以知道,最终是通过 Keyframes#getValue,估值器计算最终的动画值(如果是 int 为 IntKeyframeSet)。
  3. 最后 mUpdateListeners#onAnimationUpdate 通知更新

ObjectAnimator#animateValue

如果是 ObjectAnimator,重写了 animateValue 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ObjectAnimator Android29
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);
    }
}

// PropertyValuesHolder Android29
void setAnimatedValue(Object target) {
   if (mProperty != null) {
       mProperty.set(target, getAnimatedValue());
   }
   if (mSetter != null) {
     mTmpValueArray[0] = getAnimatedValue();
     mSetter.invoke(target, mTmpValueArray);
  }
}

可以看到 ObjectAnimator 相比较于 ValueAnimator,调用了 ValueAnimator 的 animateValue 计算动画值和通知更新后,还调用了 PropertyValuesHolder#setAnimatedValue 更新到对应 target 的属性值 (调用 Property 的 set 或反射 target 的 setter 方法)

总结

  1. ObjectAnimator#autoCancelBasedOn:相同属性,相同 target 的动画,防止出现多个动画同时更新 Target 的属性,出现错乱。不过这一行为默认是关闭的,设置 ObjectAnimator.setAutoCancel(true) 来打开
  2. AnimationHandler.addAnimationFrameCallback 向 Choreographer 注册 Choreographer.FrameCallback 回调,通过该回调获得渲染时间脉冲的回调;通过系统的 vsync 垂直同步信号来协调 cpu,gpu 和渲染的时序;Choreographer 获得 vsync 信号后 根据 当前帧的纳秒来查找哪些 Choreographer.FrameCallback 会被执行。
  3. 执行 AnimationHandler.doAnimationFrame() 方法,开始真正的动画逻辑。
  4. ValueAnimator.animateBasedOnTime(time) 执行,通过 TimeInterpolator 计算最终的 时间流逝比 fraction,然后调用 PropertyValuesHolder.calculateValue(fraction) 计算属性的值,并回调 AnimatorUpdateListener.onAnimationUpdate() 方法。
  5. PropertyValuesHolder 调用 Keyframes.getIntValue(fraction),这中间又使用到估值器 TypeEvaluator 和 Keyframe 最终结算处我们需要的属性值。
  6. 然后 ObjectAnimator 调用 PropertyValuesHolder.setAnimatedValue(target) 来更新 target 的属性值。

Ref

本文由作者按照 CC BY 4.0 进行授权