1.创建属性动画
通常我们可以通过ObjectAnimator.ofXX()的各种方法创建属性动画,所以我们可以以其中一个方法ofFloat
为例进行说明。
val imageView = ImageView(context)
val anim = ObjectAnimator.ofFloat(imageView, "translationY",0f, 100f)
上面就是调用ObjectAnimator
的静态方法来创建动画对象。下面说明一下各个参数的作用。
//ObjectAnimator.java
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
- target: 动画作用的对象,可以是任何类型的对象
- propertyName: 属性名,动画执行时,这个属性的值将会被改变
- values: 可以认为是执行动画过程,动画变化的取值范围。
进到ObjectAnimator的源码中,我们可以看到,ofFloat
这个方法做了以下操作:
- 传入target和propertyName参数到ObjectAnimator的构造函数中,创建ObjectAnimator对象。
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
- 往ObjectAnimator对象中设置动画值。
//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) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {//第一次设置mProperty肯定是为null,所以走到这里
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
这里主要看PropertyValuesHolder.ofFloat(mPropertyName, values)
//PropertyValuesHolder.java
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 = (Keyframes.FloatKeyframes) mKeyframes;
}
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
然后就来到了KeyFrameSet中,这里就是保存KeyFrame的集合,KeyFrame就是:在float的变化范围内(0f~1f),在这个范围内每个float值所对应的数值(这个数值是我们设置进来的values)。
//KeyFrameSet.java
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);//默认第一个数值为0
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);//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) {
////把values里面的值一个一个拿出来放在KeyFrame中封装起来
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);
}
初始化属性动画的过程可以用以下时序图表示:
2. 开始属性动画
anim.start()
//ObjectAnimator.java
@Override
public void start() {
//应该是取消一些操作,这部分源码可以略过 AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {//debug部分也可以略过
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();//看父类的start的方法
}
//ValueAnimator.java
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...//这里略过一些代码
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 = -1;
mFirstFrameTime = -1;
mStartTime = -1;
//需要关注的第一点
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// 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);
}
}
}
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
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();
}
}
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
- 首先看需要关注的第一个点
addAnimationCallback
,它是添加callback到AnimatorHandler中,前面getAnimationHandler().addAnimationFrameCallback(this, delay)
,添加的callback的实现就是在ValueAnimator中:
//AnimatorHandler.java
/**
* Register to get a callback on the next frame after the delay.
*/
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
找到AnimationFrameCallback
在ValueAnimator
的实现,看源码这个callback会被post到Choreographer中,这里不展开。
public final boolean doAnimationFrame(long frameTime) {
...//省略一些代码
if (!mRunning) {
// If not running, that means the animation is in the start delay phase of a forward
// running animation. In the case of reversing, we want to run start delay in the end.
if (mStartTime > frameTime && mSeekFraction == -1) {
// This is when no seek fraction is set during start delay. If developers change the
// seek fraction during the delay, animation will start from the seeked position
// right away.
return false;
} else {
// If mRunning is not set by now, that means non-zero start delay,
// no seeking, not reversing. At this point, start delay has passed.
mRunning = true;
startAnimation();
}
}
if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
mLastFrameTime = frameTime;
// 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);
//animateBasedOnTime这里需要关注一下
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
- 需要关注的第二个重点
startAnimation()
,看代码知道它是在ValueAnimator
中调用initAnimation
,但这里需要注意的是,initAnimation
方法上面有一个注解,由于ValueAnimator是父类,所以我们要看一下子类是否有重写这个方法,来到ObjectAnimator
可以看到如下:
@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();
}
}
可以看到初始化动画的时候,这里是先拿到target,而这个target在创建ObjectAnimator的时候,我们已经设置进去了。然后再通过PropertyValuesHolder调用setupSetterAndGetter
这个方法,mValues里面存放的就是PropertyValuesHolder对象,mValues的值也是在创建ObjectAnimator对象的时候设置进去了。
这里就来到了PropertyValuesHolder
中
void setupSetterAndGetter(Object target) {
if (mProperty != null) {
// check to make sure that mProperty is on the class of target
try {
Object testValue = null;
List 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 (testValue == null) {
testValue = convertBack(mProperty.get(target));
}
kf.setValue(testValue);
kf.setValueWasSetOnStart(true);
}
}
return;
} catch (ClassCastException e) {
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
mProperty = null;
}
}
// We can't just say 'else' here because the catch statement sets mProperty to null.
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
setupSetter(targetClass);
}
List 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;
}
}
try {
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
void setupSetter(Class targetClass) {
Class> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
private Method setupSetterOrGetter(Class targetClass,
HashMap> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null;
synchronized(propertyMapMap) {
// Have to lock property map prior to reading it, to guard against
// another thread putting something in there after we've checked it
// but before we've added an entry to it
HashMap propertyMap = propertyMapMap.get(targetClass);
boolean wasInMap = false;
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
setterOrGetter = propertyMap.get(mPropertyName);
}
}
if (!wasInMap) {
//这里拿到setter方法
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap();
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;
String methodName = getMethodName(prefix, mPropertyName);
...//省略一些代码
}
//这个方法就是通过我们之前传进来的属性名来拼接成setXX方法名
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;
}
可以看出子类的initAnimation()
方法就是通过反射拿到目标对象的setter方法。
然后我们到父类ValueAnimator
的initAnimation()
方法可以看到,它调用的是PropertyValuesHolder
的init()方法,里面是给KeyFrames设置估值器,也就是在动画执行时,用于计算动画数值的。
走到这里,动画的初始化基本就完成了。
完成的工作主要是这两个方面:
- 设置关键帧
- 反射拿到setter方法
3.回调更新对象属性
上面有提到动画的执行是在回调doAnimationFrame
中,我们重点关注里面调用到的方法animateBasedOnTime
//ValueAnimator.java
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
final long scaledDuration = getScaledDuration();
final float fraction = scaledDuration > 0 ?
(float)(currentTime - mStartTime) / scaledDuration : 1f;
final float lastFraction = mOverallFraction;
final boolean newIteration = (int) fraction > (int) lastFraction;
final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
(mRepeatCount != INFINITE);
if (scaledDuration == 0) {
// 0 duration animator, ignore the repeat count and skip to the end
done = true;
} else if (newIteration && !lastIterationFinished) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
} else if (lastIterationFinished) {
done = true;
}
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
//这里是重点
animateValue(currentIterationFraction);
}
return done;
}
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//这里又到了PropertyValuesHolder mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
//PropertyValuesHolder.java
void calculateValue(float fraction) {
//通过fraction拿到value,mKeyframes的value之前初始化ObjectAnimator的时候就设置进去了
Object value = mKeyframes.getValue(fraction);
//计算出动画的值
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
上面就是那个更新的值的过程,那么更新的动画值在哪里设置进去并更新对象的属性呢?我们再看一下ValueAnimator
的animateValue方法,这里有个坑就是ObjectAnimator
也重写了这个方法,刚开始哪里都是找不到更细属性值的地方,原来是在ObjectAnimator
里面调用了,所以我们来到这里
//ObjectAnimator.java
@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) {
//计算后的值是更新到PropertyValuesHolder中,在这里调用了设置动画值 mValues[i].setAnimatedValue(target);
}
}
//PropertyValuesHolder.java
@Override
void setAnimatedValue(Object target) {
if (mFloatProperty != null) {
mFloatProperty.setValue(target, mFloatAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mFloatAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
return;
}
if (mSetter != null) {
//之前我们有看到通过反射拿到了setter方法,然后这里就是通过反射去更新属性值了
try {
mTmpValueArray[0] = mFloatAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
到这里,属性动画从创建、启动到更新属性值的大概过程已经梳理出来了。最后放上动画启动的源码调用时序图。