一 简述

补间动画是Android诞生便支持的动画,也是App开发中最常用的动画,目前Android支持四种补间动画:位移(TranslateAnimation),旋转(RotateAnimation),缩放(ScaleAnimation)和透明(AlphaAnimation)。补间动画主要作用对象是Android的View。核心类的关系如下图所示:

Android补间动画原理介绍_第1张图片

 

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补间动画分析结束。最后附上动画执行的时序图:Android补间动画原理介绍_第2张图片

更多相关文章

  1. Android之动画Animation的使用
  2. android 简单动画之 animtion
  3. Ubantu下搭建Android CTS 兼容性测试环境及单项测试操作步骤
  4. 2019-12-16 Android中的动画
  5. Android SVG动画PathView源码解析与使用教程(API 14)
  6. Android Studio 导出 .aar包的操作流程
  7. Android启动画面实现
  8. 补间动画--透明渐变XML

随机推荐

  1. Robot Framework如何对Android的控件定位
  2. android 中文语音
  3. 8个常用的Android开发工具
  4. Android NDK OverView翻译
  5. Android中shape的简单介绍
  6. Android使用MediaRecorder实现录音功能
  7. Android training–android studio
  8. Android应用程序键盘(Keyboard)消息处理机
  9. bindService
  10. Android入门教程(四)之------Android工程