介绍
安卓的动画有:View动画,帧动画和属性动画。其中View动画只是让View的影像发生变化,并不能真的改变View的属性,例如平移动画后的View并不能响应点击事件,而且也只有平移、缩放、旋转和透明度四种动画。帧动画是按照顺序播放一组定义好的图片,这种动画比较简单,但是如果图片过大会引起OOM。属性动画能作用于任何对象,而且能真正的改变对象的属性,使用简单且灵活,它对应于ValueAnimator和ObjectAnimator,ObjectAnimator继承自ValueAnimator,ValueAnimator可以不指定对象,ObjectAnimator需要指定动画作用的对象。
以ObjectAnimator为例,下面代码让一个Button在3秒内沿X方向平移200像素
ObjectAnimator animator = ObjectAnimator
.ofFloat(btn, "translationX", 200)
.setDuration(3000);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
源码分析
下面进入源码,分析下属性动画的原理
ObjectAnimator.ofFloat方法
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
这里首先初始化了ObjectAnimator对象,并记录了动画作用的目标对象和属性
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
在初始化ObjectAnimator后,又调用了其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);
}
}
该方法的入参是可变参数,是我们在ofFloat方法中传入的参数。上例中只传了一个参数,它会作为可变参数的最后一项,如果未指定起始值,程序会获取该属性的起始值。
这里mProperty为空,走到else分支。分支中调用PropertyValuesHolder.ofFloat方法构建FloatPropertyValuesHolder对象,并将其传入到setValues方法中。PropertyValuesHolder对象是对属性以及该属性要变化的值的封装,这些值的类型为Float时对应着FloatPropertyValuesHolder对象。
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;
}
下面看对values的处理,调用父类PropertyValuesHolder的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);
}
这里有一个新的对象Keyframe,其名为关键帧。它是一个抽象类,可以通过工厂方法得到特定类型的实现类,这里是FloatKeyframe。这个对象包装了动画的时间以及对应的值,该时间是当前动画执行时间相对于动画总时间的进度比例。这里会初始化一个最小长度为2的Keyframe数组,然后判断传入的values长度为1的话,会构造一个Keyframe对象赋值给数组第一个元素。如果values长度大于1的话,遍历构造Keyframe对象并赋值给数组。最后使用Keyframe数组构造FloatKeyframeSet对象返回。
FloatPropertyValuesHolder对象构建完成,继续回到ObjectAnimator中将其传入setValues方法
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添加到一个HashMap中管理。
setDuration方法
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
这个方法就是设置到动画的执行时长,为mDuration属性赋值。
setInterpolator方法
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
这里采用策略设计模式,将差值器从外界注入,默认为加速减速差值器。差值器决定了属性值随时间的变化情况,比如是加速还是加速等,在计算属性值时用到。
start方法
@Override
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
//..省略log代码
super.start();
}
第一句代码是根据mAutoCancel标志位以及当前动画和前面的动画是否有相同的对象和属性,如果标志位设为true并且后面条件满足的话,就会自动取消动画。
调用父类start方法
@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");
}
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
//...
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation();
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
调用addAnimationCallback添加动画回调
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
//...
}
判断mAnimationCallbacks集合size为0即属性动画第一次启动时,这里会调用mChoreographer的postFrameCallback方法添加VSYNC的刷新信号回调,然后再添加动画的AnimationFrameCallback回调。
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
这个就是接收到垂直信号的回调,判断当前还存在动画回调的话,再将这个mFrameCallback回调添加到mChoreographer以继续进行垂直信号的回调。这里doAnimationFrame方法的逻辑留在后面分析,我们继续看start方法接下来的逻辑。
没有设置延迟,延迟时间为0,直接调用startAnimation方法
private void startAnimation() {
//...省略log代码
mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
调用initAnimation方法,并且回调外部设置的listener的onAnimationStart方法。
@CallSuper
@Override
void initAnimation() {
if (!mInitialized) {
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();
}
}
遍历mValues数组,调用PropertyValuesHolder的setupSetterAndGetter方法
void setupSetterAndGetter(Object target) {
//...省略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<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;
}
}
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());
}
}
}
}
}
如果mSetter或者mGetter为空的话,就分别调用setupSetter和setupGetter通过反射获取到对应的方法。初始化get方法后,调用目标对象的get方法获取到属性的初始值并转化为set方法需要的类型,赋值给KeyFrame。
调用父类ValueAnimator的initAnimation
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
遍历mValues数组为PropertyValuesHolder的KeyFrameSet设置估值器对象。看完了startAnimation的逻辑,我们继续回到start方法中,接下来执行setCurrentPlayTime
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);
}
方法的最后调用了animateValue方法
@CallSuper
@Override
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
调用父类的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);
}
}
}
遍历PropertyValuesHolder数组并调用calculateValue方法,最后调用动画更新回调方法。
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
调用FloatKeyframeSet的getFloatValue方法
@Override
public float getFloatValue(float fraction) {
if (fraction <= 0f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
} else if (fraction >= 1f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
// Apply interpolator on the proportional duration.
if (interpolator != null) {
intervalFraction = interpolator.getInterpolation(intervalFraction);
}
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}
在这里会计算动画过程中的属性值,首先根据时间流逝的比例通过差值器计算属性变化的比例,然后再通过估值器计算出当前的属性值返回。
得到属性值后再回到ObjectAnimator的animateValue方法,遍历mValues并调用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());
}
}
}
最后调用目标对象的set方法将为目标对象的属性进行了赋值,这样就完成了目标对象属性值的改变。
前面还有个方法没分析,在接收到垂直信号回调时调用了doAnimationFrame方法
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback == null) {
continue;
}
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
遍历动画回调,调用了回调的doAnimationFrame方法,该方法在ValueAnimator中实现
public final boolean doAnimationFrame(long frameTime) {
//...省略代码
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
调用animateBasedOnTime方法
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;
}
最后又调用到了熟悉的animateValue方法,于是随着每一个垂直信号的到来都会进行属性值的计算和赋值。该方法有一个布尔类型的返回值,表示当前动画是否结束,如果结束调用endAnimation方法。
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
removeAnimationCallback();
mAnimationEndRequested = true;
mPaused = false;
boolean notify = (mStarted || mRunning) && mListeners != null;
if (notify && !mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners();
}
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
if (notify && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
}
在该方法中移除了动画的回调,将相关参数值重置,最后调用了动画结束的回调方法通知外界动画执行完毕。
网友评论