一 简述
补间动画是Android诞生便支持的动画,也是App开发中最常用的动画,目前Android支持四种补间动画:位移(TranslateAnimation),旋转(RotateAnimation),缩放(ScaleAnimation)和透明(AlphaAnimation)。补间动画主要作用对象是Android的View。核心类的关系如下图所示:
Animation是一个抽象类,Interpolator用来描述动画的执行过程,Transformation用来描述某一时刻动画参数(位移,缩放比例,旋转角度,透明度,裁剪区域等),其相关核心代码主要在android.view.animation包下。
二 基本流程介绍
1.简单示例
TextView textView = (TextView) findViewById(R.id.testTV);
TranslateAnimation translateAnimation = new TranslateAnimation(10, 50, 10, 1080);
translateAnimation.setDuration(2000);
translateAnimation.setFillEnabled(true);
translateAnimation.setFillAfter(true);
translateAnimation.setFillBefore(true);
translateAnimation.setRepeatCount(2);
translateAnimation.setStartOffset(1000);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
textView.startAnimation(translateAnimation);
在创建补间动画的时候,我们也可以使用XML配合AnimationUtils快速创建一个Animation
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.XXX);
2.基于上述的示例分析补间动画的执行过程
两种创建补间动画的原理其实都是一致的,都是先实例化一个Animation对象,然后通过它的setXXX方法设置相关的必要参数,最后需要将实例化的Animation与一个需要执行动画的View绑定,最后由View相关的机制驱动动画的执行。关键部分便是与View的绑定和驱动过程:
/**
* Start the specified animation now.
*
* @param animation the animation to start now
*/
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);//设置动画的开始时间为-1
setAnimation(animation);//将Animation和View绑定,记录到mCurrentAnimation
invalidateParentCaches();
invalidate(true);//执行View重绘操作,可以将它认为是动画的驱动操作
}
invalidate调用其实就是通过View Tree层层调用到ViewRootImpl的invalidateChildInParent并最后执行scheduleTraversals操作。熟悉Android View绘制的应该了解,scheduleTraversals会注册一个Vsync信号并等待Vsync来触发View 的三大操作:测量,布局和绘制。那么动画具体是什么时候执行的呢?
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......
final Animation a = getAnimation();//获取当前View需要执行的动画
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);//计算当前帧的动画,more表示是否需要执行更多帧的动画
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();//获取当前帧的Transformation
} else {
......
}
......
float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
if (transformToApply != null//开始将动画的Transformation设置到当前View的Convas中去,直接影响View最终在屏幕上的显示效果
|| alpha < 1
|| !hasIdentityMatrix()
|| (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (transformToApply != null || !childHasIdentityMatrix) {
int transX = 0;
int transY = 0;
if (offsetForScroll) {
transX = -sx;
transY = -sy;
}
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());//设置Convas的显示效果的矩阵
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1) {
alpha *= transformAlpha;
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
}
......
} else if ((mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
onSetAlpha(255);
mPrivateFlags &= ~PFLAG_ALPHA_SET;
}
......
return more;
}
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
final boolean initialized = a.isInitialized();
if (!initialized) {
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());//初始化动画
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
final Transformation t = parent.getChildTransformation();
boolean more = a.getTransformation(drawingTime, t, 1f);//获取当前帧的Transformation信息
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
}
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
invalidationTransform = t;
}
if (more) {//继续下一帧动画执行
if (!a.willChangeBounds()) {
if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
} else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
parent.invalidate(mLeft, mTop, mRight, mBottom);
}
} else {
if (parent.mInvalidateRegion == null) {
parent.mInvalidateRegion = new RectF();
}
final RectF region = parent.mInvalidateRegion;
a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
invalidationTransform);
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
final int left = mLeft + (int) region.left;
final int top = mTop + (int) region.top;
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
return more;
}
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {//当我们没有明确指定动画的执行事件的时候,一般动画第一次执行的时候mStartTime=-1
mStartTime = currentTime;
}
final long startOffset = getStartOffset();//获取动画执行的延迟时间,一般没有指定的话,都是0
final long duration = mDuration;//获取动画的执行总时长
float normalizedTime;//动画的执行进度,取值在0~1之间
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
final boolean expired = normalizedTime >= 1.0f || isCanceled();//表示动画已执行完,或者已被取消
mMore = !expired;//mMore表示是否需要执行下一帧动画
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);//mFillEnabled相关的判断逻辑
//mFillEnabled=true,保持之前计算的normalizedTime值
//mFillEnabled=false,修正normalizedTime值到0~1之前
//结合上面的分析,mFillEnabled=false的话,不考虑mFillBefore和mFillAfter的值
//mFillEnabled=true的话,normalizedTime的值将有可能取值在0~1范围之外
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
if (!mStarted) {
fireAnimationStart();
mStarted = true;
if (NoImagePreloadHolder.USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);//当mFillEnabled=true,mFillBefore=true的情况下就要再次修正时间了
if (mCycleFlip) {//开启repeat的情况
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);//调用动画的applyTransformation,当我们自定义动画的时候,一般都需要重写这个方法,实现自己的动画效果
}
if (expired) {//结束或者取消动画的相关逻辑
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
if (!mMore && mOneMoreTime) {
mOneMoreTime = false;
return true;
}
return mMore;
}
从上述的分析,我们可以看出补间动画的执行是由View的绘制驱动的,详细的说就是注册-接收Vsync事件,当需要View执行的动画的时候便会走View绘制流程注册一个Vsync信号,在Vsync来了之后绘制对应时间点关键帧的View显示信息,如果动画未执行结束或者未被取消,则再次走绘制流程注册一个Vsync信号,直到动画执行结束。同时补间动画的执行并未真正改变View的实际属性,改变是View最终的显示效果,具体通过Convas相关设置得以实现。至此Android补间动画分析结束。最后附上动画执行的时序图: