Android源码解析-动画篇
注:
本篇文章将补充上篇文章中所遗留的知识点,本篇文章里的很多只是点,都依赖于上一次作者的源码分析,因此,如果读者尚未有知识积累,希望读者阅读 Android源码解析(一)动画篇-- Animator属性动画系统 后,再阅读此文章。
ObjectAnimator类:
本篇文章以
ObjectAnimator类为主题,主要说明这个类在属性动画中的重要性。这里说的重要性并不代表这个类在整个属性动画系统的实现中有多么核心的地位 (因为整个属性动画的核心,还是依赖于ValueAnimator和AnimatorHandler所构建的消息和计算流程)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中的。这就是为什么ObjectAnimator以Object打头而不是以View打头。那么这样又存在另外的问题,既然我要注入的是一个可选择的对象,我又如何告诉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.
*/
大致意思就像它的名字一样,是一个属性的抽象,不过这个属性可以通过
set和get的 方式来获取,这么说或许有点抽象,我们不妨看下我们传入的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实现了setValue和get方法,它就像一个代理类,帮你获取 "以View为Target目标对象" 的属性和设置这个目标对象的属性值。
属性值的注入:
基于上面我们的知识储备,我们来看下
ObjectAnimator.start()方法:
@Override
public void start() {
...
super.start();//ValueAniamtor.start
}
可以看出,
ObjectAnimator.start()实际上,最终调用的还是它的父类ValueAniamtor的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());
}
}
由于我们刚开始传入的
Property是View.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);
}
因此,
ObjectAnimator的setAnimatedValue方法将调用到View.TRANSLATION_X.setValue方法,从而就完成了属性的注入:
public void setValue(View object, float value) {
object.setTranslationX(value);
}