前言
《Android开发艺术探索》第三章弹性滑动中有这么一段话:”如何实现弹性滑呢?实现方法有很多,但它们都有一个共同的思想:将一次大的滑动分成若干次小的滑动并在一个时间段内完成,其实现方式有很多种,Scroller、Handler.postDelayed()、Thread.sleep()等“;我们将这段话套用到Android动画上:将一次大的属性变化(如透明度从1到0)分成很多次小的属性变化并在一个时间段内完成(16.7ms内完成),从而实现透明度渐变动画效果,其实现方式是通过Choreographer来完成的;Choreographer是动画原理的一个核心,搞明白动画工作原理之前我们需要先了解Choreographer工作过程;
Choreographer工作过程简单描述如下:在Choreographer对象中有四条链表,分别保存着待处理的输入事件,待处理的动画事件,待处理的View绘制事件、待处理的post到Choreographer中事件,Android系统每隔16.7ms发出VSYNC信号,Choreographer.FrameDisplayEventReceiver收到信号后调用onVsync方法,最终会调用Choreographer.doFrame()方法,在doFrame方法中处理输入事件、动画事件、View绘制、post到Choreographer中的FrameCallback;我们可以使用Choreographer#postFrameCallback设置callback与Choreographer交互,设置的FrameCallCack(doFrame方法)会在下一个frame被渲染时触发(即下一个VSYNC到来时执行);Choreographer更多内容请参考Android Choreographer 源码分析
Android动画原理简单描述:将View的一次大的属性变化拆分为多次小的属性变化,在每次VSYNC信号到来时,根据当前时间和插值器来计算当前View属性的值,然后给View设置该属性值,直到动画执行完毕。其中Choreographer将动画拆分成一次次小的属性变化,Choreographer是动画的指挥者。理想情况下,属性刷新次数(动画拆分为多次小的属性变化) = 动画执行时间/16.7ms 。
我们通过属性动画工作流程来介绍Android动画工作原理,其它动画的工作过程也应该类似,感兴趣的可以阅读源码来了解其原理;本文主要围绕以下四个问题来讲解动画原理:
问题一:动画如何完成一次属性变化刷新?
问题二:动画如何被拆分成一次次小的属性变化?
问题三:动画如何跳出属性刷新的流程,从而结束动画?
问题四:动画监听器何时被调用?
属性动画简单例子
在讲解原理前,我们先来看一下属性的动画的一个简单例子:
public class AnimationActivity extends AppCompatActivity implements View.OnClickListener{
private Button mButton;
public static final String TAG = "AnimationTest";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.animation_layout);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//ObjectAnimator extends ValueAnimator;
ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f);
alphaAnimation.setDuration(100);
alphaAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//属性刷新
Log.d(TAG, "onAnimationUpdate: " + animation.getAnimatedValue());
}
});
alphaAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//动画开始
Log.d(TAG, "onAnimationStart: ");
}
@Override
public void onAnimationEnd(Animator animation) {
//动画结束
Log.d(TAG, "onAnimationEnd: ");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.d(TAG, "onAnimationCancel: ");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.d(TAG, "onAnimationRepeat: ");
}
});
alphaAnimation.start();
}
}
运行之后,我们点击Button,对Button开始做透明度动画,相关日志打印如下:
01-28 10:15:39.587 3867-3867/com.android.animation D/AnimationTest: onAnimationStart:
01-28 10:15:39.587 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 1.0
01-28 10:15:39.597 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 1.0
01-28 10:15:39.607 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.93037105
01-28 10:15:39.627 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.75452065
01-28 10:15:39.647 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.5
01-28 10:15:39.657 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.24547923
01-28 10:15:39.677 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.061846733
01-28 10:15:39.697 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.0
01-28 10:15:39.697 3867-3867/com.android.animation D/AnimationTest: onAnimationEnd:
动画原理分析
问题一:动画如何完成一次属性变化刷新?
下面我们从动画的入口alphaAnimation.start()开始分析动画原理,先来看动画如何完成一次属性刷新:
private void start(boolean playBackwards) {
//设置相关标志位
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
//最关键的方法,动画循环执行在该方法中实现
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.
//初始化相关属性,onAnimationStart在该方法中被调用;
startAnimation();
}
}
以上最核心的方法是AnimationHandler.addAnimationFrameCallback()。下面我们来分析该方法:
AnimationHandler
//final AnimationFrameCallback callback就是ValueAnimation
//public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
//动画开始执行时,mAnimationCallbacks.size()为0.会调用getProvider().postFrameCallback方法;
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
//将ValueAnimator添加在mAnimationCallbacks列表;
mAnimationCallbacks.add(callback);
}
}
下面我们来看getProvider().postFrameCallback(mFrameCallback);
AnimationHandler.getProvider()方法、MyFrameCallbackProvider、mFrameCallback代码如下:
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
/**
* Default provider of timing pulse that uses Choreographer for frame callbacks.
*/
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
//最核心的大佬编舞者终于现身了
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
}
@Override
public long getFrameTime() {
//获取上一次VSYNC信号时间;
return mChoreographer.getFrameTime();
}
}
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//Choreographer收到VSYNC都会调用该方法,正常情况下每16.7ms就会调用一次doFrame
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
//再将自己post到编舞者中,以便下次收到VSYNC时,继续调用doFrame来刷新动画;
getProvider().postFrameCallback(this);
}
}
};
在执行完getProvider().postFrameCallback(mFrameCallback)后,下一次收到VYSNC信号时,就会调用mFrameCallback.doFrame方法,doFrame方法主要做了两件重要的工作:
(1)调用doAnimationFrame刷新View的相关属性;
(2)将自己再次post到Choreographer中,这样在下一次VSYNC到来时继续执行doFrame,完成下一次属性刷新;
关键点:动画属性一次次刷新就是通过mFrameCallback来实现的;
下面我们来看doAnimationFrame(getProvider().getFrameTime())方法:
private void doAnimationFrame(long frameTime) {
for (int i = 0; i < size; i++) {
//callback就是我们添加到mAnimationCallbacks列表中的ValueAnimator
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (isCallbackDue(callback, currentTime)) {
//执行ValueAnimator.doAnimationFrame;
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
//每一次刷新刷新之后会调用,目前没有看到会执行该方法;
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
AnimationFrameCallback callback = mAnimationCallbacks.get(i);该callback就是valueAnimator,即我们在调用AnimationHandler.addAnimationFrameCallback中将ValueAnimator添加在mAnimationCallbacks列表;
下面我们来看ValueAnimator.doAnimationFrame(frameTime)方法,在该方法最终会刷新View的属性;
/**
* Processes a frame of the animation, adjusting the start time if needed.
* 处理每一帧中的刷新
* @param frameTime The frame time.
* @return true if the animation has ended.
* @hide
*/
public final void doAnimationFrame(long frameTime) {
//在animateBasedOnTime刷新View属性,并判断动画是否执行完毕;
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
//如果动画结束执行endAnimation
endAnimation();
}
}
下面我们继续来看animateBasedOnTime()方法:
boolean animateBasedOnTime(long currentTime) {
//done表示动画是否执行完毕;
boolean done = false;
//在ValueAnimator.startAnimation中将mRuning置为true;
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;
//根据当前时间currentTime来判断动画是否执行完毕;
final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
(mRepeatCount != INFINITE);
if (scaledDuration == 0) {
...
} else if (newIteration && !lastIterationFinished) {
...
} else if (lastIterationFinished) {
//动画执行完毕;
done = true;
}
//计算当前View属性值,然后设置View相关属性;
animateValue(currentIterationFraction);
}
return done;
}
我们继续来看ObjectAnimator和ValueAnimator的animateValue方法,ObjectAnimator继承ValueAnimator;
ObjectAnimator:
void animateValue(float fraction) {
//getTarget()返回的就是ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f)中的mButton,
final Object target = getTarget();
//调用ValueAnimator.animateValue,主要是计算当前动画的进度
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//调用PropertyValuesHolder.setAnimatedValue设置mButton在该帧的透明度,即super.animateValue(fraction)计算出的当前透明度
mValues[i].setAnimatedValue(target);
}
}
VauleAnimator:
void animateValue(float fraction) {
//根据插值器计算当前属性动画的进度
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//将当前进度存放到PropertyValuesHolder.mAnimatedValue,
//在AnimatorUpdateListener.onAnimationUpdate方法中可以通过ValueAnimator.getAnimatedValue获取动画进度
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
//回调AnimatorUpdateListener.onAnimationUpdate方法
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
animateValue方法主要工作:根据插值器计算mButton在该帧时的透明度,然后调用PropertyValuesHolder.setAnimatedValue设置mButton的透明度,从而完成该帧透明度变化;每一帧更新一次透明度,最终完成整个透明度变化过程;
下面我们来看PropertyValuesHolder.setAnimatedValue(Object target)代码,其中target就是ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f)中的mButton,该方法主要就是设置当前View的属性值,执行该方法最终会调用View.setAlpha()来设置mButton的透明度,在该帧中mButton的透明度就完成了更新。
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
* to handle turning the value calculated by ValueAnimator into a value set on the object
* according to the name of the property.
* @param target The target object on which the value is set
* 通过注释可以看出,该方法主要就是设置当前View的属性值,执行完该方法后,在该帧中mButton的透明度就完成了更新
*/
void setAnimatedValue(Object target) {
// 如果有属性,通过set方法来更新属性值
if (mProperty != null) {
//getAnimatedValue()就是返回VauleAnimator.animateValue中计算出的当前进度值
mProperty.set(target, getAnimatedValue());
}
// 是否通过反射调用属性的setter方法来更新属性值
if (mSetter != null) {
try {
//getAnimatedValue()就是返回VauleAnimator.animateValue中计算出的当前进度值
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
}
}
}
以上流程完成了mButton透明度的一次更新,同时也解释了动画是如何完成一次属性变化的刷新。
问题二:动画如何被拆分成一次次小的属性变化?
该问题也可以简单理解如何循环更新mButton透明度呢?我们再来回顾AnimationHandler.addAnimationFrameCallback方法中:
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
}
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//在doAnimationFrame更新mButton在当前帧中的透明度
doAnimationFrame(getProvider().getFrameTime());
//mAnimationCallbacks列表中存放正在执行的动画,动画执行完毕会将对应的AnimationFrameCallback remove掉
if (mAnimationCallbacks.size() > 0) {
//将mFrameCallback自身再次post到Choreographer在下一次VSYNC信号到来时,再次调用doFrame来更新mButton的透明度
//如果动画一直没有执行完毕,会一直循环执行doFrame
getProvider().postFrameCallback(this);
}
}
};
在mFrameCallback中完成多次动画的拆分,并在每次收到VSYNC信号时,更新View的属性,连续不停的更新View属性,直到动画结束。
问题三:动画如何跳出属性刷新的流程,从而结束动画?
该问题也可以理解为动画是如何跳出mFrameCallback循环,在mFrameCallback中会判断mAnimationCallback列表中是否还有AnimationFrameCallback,如果mAnimationCallback列表中没有AnimationFrameCallback,那么透明度变化在下一次VSYNC到来时就不会再继续执行从而结束动画,那么我们找到mAnimationCallbacks列表啥时候将AnimationFrameCallback remove掉,就代表动画结束;主要流程如下:
ValueAnimator:
public final void doAnimationFrame(long frameTime) {
//animateBasedOnTime根据当前时间计算动画是否应该结束
//如果动画应该结束,返回true;
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
//调用endAnimation结束动画
endAnimation();
}
}
private void endAnimation() {
AnimationHandler handler = AnimationHandler.getInstance();
//在AnimationHandler将ValueAnimator remove掉;
handler.removeCallback(this);
if ((mStarted || mRunning) && mListeners != null) {
for (int i = 0; i < numListeners; ++i) {
//调用AnimatorListener.onAnimationEnd方法通知应用
tmpListeners.get(i).onAnimationEnd(this);
}
}
}
AnimationHandler:
public void removeCallback(AnimationFrameCallback callback) {
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) {
//将ValueAnimator remove调用,这样在下一此VSYNC到来时就不会继续执行动画
mAnimationCallbacks.set(id, null);
}
}
问题四:动画监听器何时被调用?
即以下几个方法是在何时回调的?
AnimatorListener.onAnimationStart(); //在动画开始时调用
AnimatorUpdateListener.onAnimationUpdate();//在每一次属性刷新时调用
AnimatorListener.onAnimationEnd();//动画结束时调用
通过以上代码中注释我们可以找到以下调用关系:
ValueAnimator.start() > startAnimation() > notifyStartListeners > AnimatorListener.onAnimationStart()
ValueAnimator.doAnimationFrame() > animateBasedOnTime() > AnimatorListener.onAnimationUpdate();
ValueAnimator.doAnimationFrame() > endAnimation() > AnimatorListener.onAnimationEnd();
最后,我们再来回顾一下动画原理的简单描述:将View的一次大的属性变化拆分为多次小的属性变化,在每次VSYNC信号到来时,根据当前时间和插值器来计算当前View属性的值,然后设置给View设置该属性值,直到动画执行完毕,动画的拆分是由Choreographer来完成的。
以上就是动画执行的主要过程,希望对你理解Android动画有所帮助。
参考资料
《Android开发艺术探索》
《深入理解Android卷三》第四章Android动画原理简介
Android动画原理分析
网友评论