首先我们从一个简单的属性动画例子开始
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(iv, "translationX", 1, 10);
objectAnimator.setDuration(100);
objectAnimator.start();
ObjectAnimator.ofIn方法
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
该方法的主要过程是:
- 传入属性名称,目标对象,创建一个ObjectAnimator对象
- 设置属性具体变化值
如何创建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 = 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为null
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;
}
总结起来就是:
- 保存动画操作的对象,如果之前设置过动画对象并开始了动画,则取消动画,并重置为当前的对象
- 保存动画操作的属性,如果之前有,则清空,并保存当前的属性对象。
setIntValues方法,记录属性变化的多个对应值
@Override
public void setIntValues(int... values) {
//mValues是一个 :PropertyValuesHolder[] mValues;
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) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
//mPropertyName记录是传入的属性名字
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
//具体保存值到map集合中
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;
}
这个方法主要的作用是:如果没有保存过属性以及对应的值时,那么使用mValues数组保存这个属性-对应值也就是PropertyValuesHolder对象。
PropertyValuesHolder.ofInt这个方法会根据值的类型返回多个PropertyValuesHolder对象,下文会详细讲。setValues方法会把产生的PropertyValuesHolder对象保存到mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues)
这个map集合中,保存起来。
注意:这里的mValues是一个PropertyValuesHolder类型的数组
PropertyValuesHolder.ofInt
PropertyValuesHolder类的ofXXX静态方法,主要作用是根据传入的值类型,产生不同的PropertyValuesHolder对象,目前包含IntPropertyValuesHolder
,FloatPropertyValuesHolder
等。原理大同小异,我们就看上面的ofInt
静态方法
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
创建了IntPropertyValuesHolder对象,我们在来看这个对象
public IntPropertyValuesHolder(String propertyName, int... values) {
//super方法就是简单的传入了属性名到mPropertyName属性中
super(propertyName);
setIntValues(values);
}
在看IntPropertyValuesHolder的setIntValues方法
@Override
public void setIntValues(int... values) {
//调用PropertyValuesHolder的setIntValues方法
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
看PropertyValuesHolder的setIntValues方法
public void setIntValues(int... values) {
//记录传入值的类型
mValueType = int.class;
//mKeyframes是一个Keyframes类型的对象
mKeyframes = KeyframeSet.ofInt(values);
}
这里有几个容易搞混的对象:
-
Keyframe
:首先这是一个抽象类,主要作用是保存动画每一帧对应的值,同时还保存一些其他的数据。 -
Keyframes
:是一个接口,这个接口抽象出了一个keyframe对象的一些行为 -
KeyframeSet
:这个类实现了Keyframes
接口,主要实现了每帧如何计算值,并保存了所有的Keyframe
对象。
了解了这几个对象的差异,再来看上面的KeyframeSet.ofInt方法
public static KeyframeSet ofInt(int... values) {
//传入值的总数
int numKeyframes = values.length;
//至少有两个值
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
//如果只传入一个值,那初始值为0
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
//终值是传入的值,对应参数是1
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);
}
创建并返回一个int类型的Keyframe对象
public static Keyframe ofInt(float fraction) {
return new IntKeyframe(fraction);
}
//第一个参数表示时间,其值是从0-1之间,
//第二个参数表示对应的值
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
其他一些set方法
其他一些set方法都是比较简单的方法,都是设置一些动画所需的参数,一般都是通过一个属性变量直接保存。如setDuration
,setInterpolator
等都是保存时长,插值器等参数。
总结:
动画的开始是干了如下的事:
- 把动画操作的对象保存起来,并停止和清除之前设置的对象。
- 把要操作的属性保存起来
- 根据属性动画变化过程中,把属性、属性、值类型,封装成相应的PropertyValuesHolder对象,并保存在一个PropertyValuesHolder类型的mValues数组中(可能会有多个属性参与动画)。
- PropertyValuesHolder根据值的类型有IntPropertyValuesHolder,Float PropertyValuesHolder等。这个对象主要是保存了KeyframeSet对象,这个对象保存多个KeyFrame集合,即属性值的value和实践。