在Anroid 3.0之前,Android系统给我们提供了两种动画效果方式:补间动画和帧动画,3.0之后有了属性动画,属性动画与前两者的区别在于属性动画是改变的View的实际属性。比如我们改变一个Button的位置,用属性动画平移后,View是可以响应点击事件的。而用前面两种动画则不行。
属性动画我们经常用到的是ObjectAnimator.ofFloat方法,今天我们就从这个方法入手看看源码:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
首先是新建一个ObjectAnimator实例,讲我们传进来的view和要操作的属性名保存为ObjectAnimator的全局变量。然后是对我们传入的values进行层层封装。
看看anim.setFloatValues方法的实现。
@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);
}
}
mValues是PropertyValuesHolder[] 数组,mProperty是Property类的实例,它们都还没有被初始化,所以都为null,所以下面会调用setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));这行代码。
进入该方法:
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存入到mValuesMap里面,以我们传入的属性名作为key,而我们传入的values这时已经被封装到了PropertyValuesHolder里面,是通过PropertyValuesHolder.ofFloat(mPropertyName, values)这行代码进行封装的,进入PropertyValuesHolder的ofFloat方法,
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
发现新建了一个FloatPropertyValuesHolder,将我们属性名mPropertyName和Values传了进去。很明显FloatPropertyValuesHolder是PropertyValuesHolder的子类。
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
在构造方法里接着调用了setFloatValues方法,
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
接着会调用父类的setFloatValues方法
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
看到这里好熟悉的套路啊 又跳出来个KeyframeSet的ofFloat方法
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
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);
}
这段代码很好理解,先创建了一个FloatKeyframe类型的keyframes[] 数组,接着看下面的for循环里,讲values通过Keyframe.ofFloat方法封装到了FloatKeyframe里面,然后添加到keyframes[] 数组里。最后将keyframes数组作为参数传到FloatKeyframeSet里。
回过头来再想一想,上面的这些就是对我们通过ObjectAnimator.ofFloat()方法传入的values进行了层层封装,最后封装到了FloatKeyframe这个关键帧里面,FloatKeyframeSet(的父类KeyframeSet)持有FloatKeyframe的引用,PropertyValuesHolder持有KeyframeSet的引用,而PropertyValueHolder又被存入到了ObjectAnimator(的父类ValueAnimator)的mValuesMap一个map里。
一些初始化的工作完成后,我们就会调用start方法开始执行动画,那么我们看看ObjectAnimator的start方法,调用了父类(ValueAnimator)的start方法,
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
if (mRepeatCount == INFINITE) {
// Calculate the fraction of the current iteration.
float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
mSeekFraction = 1 - fraction;
} else {
mSeekFraction = 1 + mRepeatCount - mSeekFraction;
}
}
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
mLastFrameTime = 0;
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
if (mStartDelay == 0 || mSeekFraction >= 0) {
// 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) {
// 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);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
有这么两行代码
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
执行到了这里会有一个callback回调,传入的参数是this,也就是说ValueAnimator类肯定实现了这个callback,我们去看一下 确实是实现了AnimationHandler.AnimationFrameCallback接口,那么找它的实现方法doAnimationFrame
public final void doAnimationFrame(long frameTime) {
AnimationHandler handler = AnimationHandler.getInstance();
if (mLastFrameTime == 0) {
// First 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);
}
// The frame time might be before the start time during the first frame of
// an animation. The "current time" must always be on or after the start
// time to avoid animating frames at negative time intervals. In practice, this
// is very rare and only happens when seeking backwards.
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
}
这里首先会执行startAnimation()方法,后面还会执行animateBasedOnTime()方法,
线点进去看看startAnimation()方法,
private void startAnimation() {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
我们看到有个initAnimation()方法,注意这里有个坑,按cmd键点进去这是ValueAninmator的initAnimation()方法,暂且先看一下这个方法:
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
调用了PropertyValueHolder的init()方法。
void init() {
if (mEvaluator == null) {
// We already handle int and float automatically, but not their Object
// equivalents
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
// KeyframeSet knows how to evaluate the common types - only give it a custom
// evaluator if one has been set on this class
mKeyframes.setEvaluator(mEvaluator);
}
}
我们的估值器TypeEvaluator就是在PropertyValueHolder的init方法里被设置进去的。
然而不应该执行到就结束了啊 我们还没真正的找到动画是怎么执行的呢!!执行动画起码应该是对View做点什么吧。。。我们传进来的参数被封装了也没看到怎么用的呢?
所以我们找错代码了… 坑就在上面刚说的initAnimation()这个方法。父类ValueAnimator有这个方法,但是子类ObjectAnimator也有这个方法。
我们进入ObjectAnimator的initAnimation()方法里看看 ,。
@CallSuper
@Override
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
for循环里面调用了PropertyValueHolder的setupSetterAndGetter(target)方法,找到这个方法target就是我们传入的view,而看这个方法名里有setter和getter,我们猜可能是会调用view的set和get方法。其实我理解这个方法的最终目的就是给PropertyValueHolder的两个成员变量mSetter和mGetter赋值的。这两个都是Method的实例,如果我们想通过反射去调用view的setXXX()和getXXX()方法,我们就要拿到Method,然后调用Method的invoke()方法。当然我们还需要知道方法名,比如View.setTranslationX()方法。我们使用ObjectAnimator.ofFloat(view,”translationX”,values),这里传入的translationX就是用来获取方法名的,源码里是通过getMethodName方法来获取的实现方式如下:
String methodName = getMethodName("set", mPropertyName);
static String getMethodName(String prefix, String propertyName) {
if (propertyName == null || propertyName.length() == 0) {
// shouldn't get here
return prefix;
}
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
String theRest = propertyName.substring(1);
return prefix + firstLetter + theRest;
}
如果想获取set方法的方法名 第一个参数传入set,第二个参数就是我们的translationX, 它会截取这个字符串的第一个字符,然后设置成大写,也就是firstLetter等于大写T, 而theRest就是剩下的ranslationX,然后和prefix进行拼接 成 setTranslationX, 因此我们在写这个属性的时候搞不清首字母是大写还是小写,看到这里你就明白了 ,都可以哈。。
写到这里,这个分支是从上面的doAnimationFrame()方法里的startAnimation()方法进入顺下来的,下面再来看看另一个分支,就是在doAnimationFrame方法里startAnimation方法执行完后下面还有一个方法是animateBasedOnTime()方法,开始进入,看主要代码执行了animateValue(currentIterationFraction)方法,而这个方法也是一个坑,就是父类和子类都有这个方法,找代码的时候容易迷失方向。
看子类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);
}
}
先是调用了父类的super.animateValue方法,然后又调用了PropertyValueHolder的setAnimatedValue方法。
先从父类的super.animateValue看起。
@CallSuper
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);
}
}
}
通过插值器Interpolator计算得到fraction,然后for循环调用PropertyValueHolder的calculateValue方法,计算得到的值赋值给PropertyValueHolder的变量mFloatAnimatedValue,就是动画执行到某一时刻的值。然后进入下面的for循环 mUpdateListeners.get(i).onAnimationUpdate(this);回调。这是父类(ValueAnimator)的animateValue方法执行的事情,再回到子类(ObjectAnimator)的animateValue方法,下面有一个for循环,调用了PropertyValueHolder的setAnimatedValue方法
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());
}
}
}
mSetter.invoke(target, mTmpValueArray);
这个方法最后的调用才让我们的view执行了动画。
所以属性的动画的原理就是通过反射来改变view的属性从而让view动起来的。
结束