前言
属性动画是Android3.0之后开发的主流动画框架,开发者除了能够熟练使用它做动画效果之外,还需要近一步了解属性动画的实现原理,这里就来简单的分析一下属性动画的代码实现。
代码分析
补间动画的偏移缩放等操作都可以通过属性动画ObjectAnimator来实现,通常实现的代码如下所示:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
现在就从这个最简单的生成float类型属性动画的接口开始分析,看代码实现就是生成了一个ObjectAnimator对象并且为它设置了所有的float属性值。
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
接着查看ObjectAnimator的构造方法,构造方法中调用了setTarget和setPropertyName两个方法。
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
查看setTarget方法的实现源码发现ObjectAnimator有一个mTarget的弱引用对象,负责保持做动画对象的引用。
@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;
}
}
接着查看setPropertyName方法的实现,这个时候mValues是空对象,需要到setValues调用才会做初始化,所以只是把propertyName存储到mPropertyName属性里。
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;
}
现在开始查询最后一句anim.setFloatValues(values)的实现源码,前面已经设置了mPropertyName而且mValues为空,所以走的是setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));这一句。
@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) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
这个方法会把属性值名称和定义属性动画时的值信息一起传入到PropertyValuesHolder对象中生成一个新的Holder对象,这个对象可以保存一个属性和动画执行过程中该属性的中间值。查看PropertyValuesHolder对象的实现代码,发现ofFloat其实返回了PropertyValuesHolder的一个子内部类也就是FloatPropertyValuesHolder类的对象。
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
查看FloatPropertyValuesHolder类的构造方法,它首先记录了需要执行动画的属性,之后调用setFloatValues生成了KeyFrameSet对象,也就是关键帧集合对象。
// PropertyValuesHolder的构造方法
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
// FloatPropertyValuesHolder的构造方法
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
// PropertyValuesHolder的setFloatValues
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
// FloatPropertyValuesHolder的setFloatValues
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
查看KeyFrameSet对象的ofFloat实现源码,
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
// FloatKeyFrame数组,根据提供的float值个数判断
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
// 如果用户只提供了一个float数值,那么生成开始和结束两个关键帧
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 {
// 否则生成多个关键帧
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
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);
}
那么KeyFrame对象的实现又是怎么样的呢,查看其实现源码发现它主要记录了关键帧在动画中的位置比例,属性值和属性值类型,它们只是简单的数据保存类。
static class FloatKeyframe extends Keyframe {
// 记录当前值
float mValue;
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
public float getFloatValue() {
return mValue;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
if (value != null && value.getClass() == Float.class) {
mValue = ((Float)value).floatValue();
mHasValue = true;
}
}
}
而KeyFrameSet类也主要负责记录所有的关键帧数据,并且根据提供的动画进度值计算当前帧属性值数值。
public class KeyframeSet implements Keyframes {
// 关键帧数据
int mNumKeyframes;
// 只在单一关键帧使用
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator;
// 只在多关键帧时使用
List<Keyframe> mKeyframes;
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();
}
....
}
综合上面的代码可知,用户在ObjectAnimator里设置的值会被作为关键帧保存在PropertyValuesHolder里面,以便后续的动画播放使用之前保存的所有关键帧。接着查看setValues方法的实现,可以看出一个ObjectAnimator可以包含多个PropertyValuesHolder也就实现了同时对多个属性做动画的能力。这里暂时只考虑对单一属性做动画的情况,mValuesMap保存了每个属性值对应的PropertyValuesHolder对象。
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;
}
以上就已经将属性动画需要的数据都准备好,在设置好duration之后调用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));
}
}
// 开始执行
super.start();
}
会发现调用了super.start()方法来执行真正的动画操作。查看ObjectAnimator的实现代码可以发现它继承自ValueAnimator,查看ValueAnimator的实现发现它继承子Animator类。首先来分析一下Animator类的实现逻辑,查看源码发现它的start、stop等动画执行方法都是空实现,而它的内部包含了各种生命周期的回调对象列表,可见Animator主要做属性动画对象的各种基础数据和接口定义的任务。
// Animator类主要定义属性动画的各种实现接口和回调数据结构
public abstract class Animator implements Cloneable {
public static final long DURATION_INFINITE = -1;
ArrayList<AnimatorListener> mListeners = null;
ArrayList<AnimatorPauseListener> mPauseListeners = null;
boolean mPaused = false;
public void start() {
}
public void cancel() {
}
public void pause() {
if (isStarted() && !mPaused) {
mPaused = true;
if (mPauseListeners != null) {
ArrayList<AnimatorPauseListener> tmpListeners =
(ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationPause(this);
}
}
}
}
....
}
接下来开始查看ValueAnimator的实现逻辑,start把调用转给了start(boolean)内部实现,mStartDelay代表第一次启动的延时,通常设置还都是0延时,start内部会调用startAnimation。mSeekFraction代表当前动画执行已经流逝的百分比,默认的名mSeekFraction值是-1,后面同样会执行setCurrentPlayTime(0)。
private void start(boolean playBackwards) {
....
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
....
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation();
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
startAnimation的实现中会调用initAnimation,这个时候开始每个mValues.init方法,也就是PropertyValuesHolder的init方法。
private void startAnimation() {
mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
也就是PropertyValuesHolder的init方法会为KeyFrameSet设置属性值估值器,用来根据动画执行百分比计算属性值。
void init() {
if (mEvaluator == null) {
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
mKeyframes.setEvaluator(mEvaluator);
}
}
上面的initAnimation是ValueAnimator的实现,实际上ObjectAnimator对initAnimation做了覆盖操作,增加了将mTarget对象设置到PropertyValuesHolder里的操作。
// ObjectAnimator的initAnimation方法
void initAnimation() {
if (!mInitialized) {
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 将目标对象设置到PropertyValuesHolder里 mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
这里查看FloatPropertyValuesHolder的setupSetterAndGetter方法,实际上是把mTarget对象的mProperty属性的getter/setter方法保存到了PropertyValuesHolder中。
Method mSetter = null;
Method mGetter = null;
void setupSetterAndGetter(Object target) {
Class targetClass = target.getClass();
if (mSetter == null) {
setupSetter(targetClass);
}
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()) {
if (mGetter == null) {
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
}
}
....
}
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
private void setupGetter(Class targetClass) {
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
保存setter/getter其实就是用反射的方法得到属性的获取和设置方法,所以属性动画其实是使用反射最终改变了内部的属性值。
接着查看setCurrentPlayTime(0);的实现最终调用到了animateValue方法,它会根据流逝时间比例先用插值器计算动画运动到的比例,然后调用PropertyValuesHolder.calculate计算当前的属性值。
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
initAnimation();
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
} else {
mSeekFraction = 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);
}
}
}
PropertyValuesHolder.calculate会根据动画执行的流式时间比值,上面的方法都是ValueAnimator的实现,实际上ObjectAnimator覆盖了animateValue方法,在计算完属性值之后还会调用PropertyValuesHolder的setAnimatedValue方法。
@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);
}
}
查看PropertyValuesHolder.setAnimatedValue方法实现,可以看到正在使用mSetter方法来修改target的属性值。
void setAnimatedValue(Object target) {
if (mProperty != null) {
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());
}
}
}
前面的分析已经把动画的第一帧展示出来了,接下来该如何继续动画后面执行呢,其实在start(boolean)方法里有一个addAnimationCallback(0)方法的调用。
private void start(boolean playBackwards) {
....
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
// 添加后续回调操作
addAnimationCallback(0);
....
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation();
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
查看addAnimationCallback(0)的源码会发现它会发送一个回调到AnimatorHandler对象中,这个回调的方法就是doAnimationFrame方法。查看Provider的源码会发现那些post方法都会被委托给Choreographer对象,Choreographer内部就是使用Looper和Handler实现的,感兴趣的可以自己翻阅它的实现代码。
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
// 在这里提交了一个mFrameCallback回调
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
// mFrameCallback里实现了递归的调用doAnimationFrame
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
// 递归实现调用doAnimationFrame
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
doAnimationFrame方法会根据当前的时间来决定是否继续执行animateBasedOnTime方法,这个事件就是当前动画已经执行的时间。
public final boolean doAnimationFrame(long frameTime) {
....
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
....
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
animateValue(currentIterationFraction);
}
return done;
}
在animateBasedOnTime里又调用了animateValue方法不断的生成之后的属性值,animateValue之后的操作和第一帧的相同这里不再做分析。
总结
以上的代码执行流程就是整个属性代码的基本执行过程,整个过程可以大致总结如下:ObjectAnimator会将用户设置的targetView和values放到PropertyValuesHolder对象中管理,PropertyValuesHolder对象又会把所有的values封装到KeyFrameSet中,而每一个value正好对应一个KeyFrame关键帧对象;用户调用ObjectAnimatior.start方法后会将targetView的setter/getter方法也设置到PropertyValuesHolder对象中,之后数值的动画过程由ValueAnimator来实现,而ValueAnimator主要负责计算当前动画流逝的时间比例,在调用用户设置的插值器计算动画执行到的比例,PropertyValuesHolder通过动画执行比例和关键帧中的数据结合估值器计算出当前属性值,最后通过反射调用targetView的属性设置方法实现动画效果。