属性动画源码解析
这里主要研究ObjectAnimator 是如何改变控件的属性
用法
//第一个参数 需要改变的对象
//第二个参数 需要改变的属性的 set 方法,注意需要第一个参数拥有对应的set 方法
//第三、第四参数 改变的数值
ObjectAnimator animator = ObjectAnimator.ofFloat(new TextView(this), "scaleX", 0f, 1f);
animator.setDuration(2000);
animator.start();
我们来看ObjectAnimator.ofFloat 做了什么
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
//直接 new 了一个对象,设置值返回
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
来看 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 为Textview
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 could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
//根据注射可以知道当前mValues 为null
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
//主要将mPropertyName 赋值;根据前面 这个mPropertyName 为scaleX
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
构造方法主要就是将 mTarget 和 mPropertyName 赋值在来看 anim.setFloatValues 方法
@Override
public void setFloatValues(float... values) {
//此时mValues 为null
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
//刚开始,mProperty 也为 null
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
//所以走的是这里
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
//setValues 只是将values 的值保存到 mValuesMap 中
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;
}
又是一些保存操作,来看看PropertyValuesHolder 是怎么生成的
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
//一路进来会发现到这,将mFloatKeyframes 赋值,我们来看super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
走了一路最后发现调用的是父类的setFloatValues 方法
//PropertyValuesHolder 中
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
//生成关键帧
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
//当我们传进来的values 只有一个时,会默认添加一个0f的初始帧
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
//当values 不只有一个时,第0个为我们传进来的第0个
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
//循环 假设numKeyframes = 3 时
//(float) i / (numKeyframes - 1) 的值就为 1/2 和 1 相当于除第一个值后,后面的平均分
keyframes[i] =
(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
if (Float.isNaN(values[i])) {
badValue = true;
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
最后直接返回 FloatKeyframeSet ,这里就类似一个数据实体类,用来保存值;所以到这里主要做了一些保存值的操作,继续来看 start 方法
@Override
public void start() {
//做一些验证
AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
Log.d(LOG_TAG, " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
pvh.mKeyframes.getValue(1));
}
}
//直接调用父类的start
super.start();
}
我们发现它直接使用父类的start ,而ObjectAnimator 的父类就是ValueAnimator 进去
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
//判断是否存在 Loop 对象
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
......
addAnimationCallback(0);
.......
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
.....
startAnimation();
.........
}
}
addAnimationCallback 方法主要就是Animator 动画的数据流向,最终会回调到自身的animateValue 方法中,想了解的可以自行了解也可参照这篇博客;
接下来继续看animateValue方法;
// ObjectAnimator 中
@Override
void animateValue(float fraction) {
final Object target = getTarget();
//判断 mTarget ;这个值在前面已经设置了
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;
}
//调用父类的animateValue 主要做回调跟新操作,如开始和结束的监听
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 来看这里
mValues[i].setAnimatedValue(target);
}
}
还记得 mValues 值么,初始化的时候就赋值了
//在 ObjectAnimator.ofFloat 的时候
@Override
public void setFloatValues(float... values) {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
}
所以mValues 就是PropertyValuesHolder,所以调用的是PropertyValuesHolder.setAnimatedValue
@Override
void setAnimatedValue(Object target) {
......
if (mSetter != null) {
try {
mTmpValueArray[0] = mFloatAnimatedValue;
//通过反射调用 target 的set 方法;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
原来是通过反射来调用target 的set 方法,这个target 就是之前传进来的 TextView。那么这个 mSetter 是在哪里初始化的呢?来看startAnimation(); 方法
private void startAnimation() {
....
initAnimation();
.....
}
initAnimation(); 看名字就直到是初始化的方法,来看 ObjectAnimator 中的initAnimation
// ObjectAnimator 中
@Override
void initAnimation() {
if (!mInitialized) {
//判断target 是否为null 因为设置了所以不为 null
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//来看这里
mValues[i].setupSetterAndGetter(target);
}
}
//调用父类的 initAnimation
super.initAnimation();
}
}
又是 mValues,通过上面可以直到这里调用的就是 PropertyValuesHolder.setupSetterAndGetter方法
// PropertyValuesHolder 中
void setupSetterAndGetter(Object target) {
.....
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
//获取方法
setupSetter(targetClass);
}
......
}
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
//赋值mSetter
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null;
synchronized(propertyMapMap) {
.....
if (!wasInMap) {
//得到 setterOrGetter
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
//做缓存
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, setterOrGetter);
}
}
return setterOrGetter;
}
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
//通过拼接的得到 setXxx 的方法名
String methodName = getMethodName(prefix, mPropertyName);
Class args[] = null;
if (valueType == null) {
try {
//当前 valueType 不为null 但是处理方式都是一样的,通过class 获取对应的方法
//这属于反射内容,请自行了解
returnVal = targetClass.getMethod(methodName, args);
} catch (NoSuchMethodException e) {
// Swallow the error, log it later
}
}
.....
return returnVal;
}
到这里就知道 mSetter 的赋值时机;
总结
ObjectAnimator 动画其实就是通过获取对应类的 set 方法,然后反射调用,不断修改值来实现;而且对于第一个参数,可以是任意 Object 类,只要该类中设置又 set 方法即可