动画刷新原理

304 阅读2分钟

属性动画和补间动画共同点是都主要由三部分组成:Animation/Animator、插值器、估值器。我们今天主要分析这两种动画是如何开始,以及如何实现的周期绘制。

先看一下整体时序图,其中左边为属性动画,右边为补间动画。

整体来说这两种动画都是依赖的ChoreoGrapher来控制的刷新周期,其中属性动画依赖的消息回调类型是CALLBACK_ANIMATION,补间动画则是依附于View的绘制然后顺便做了动画的操作,依赖的消息回调类型是CALLBACK_TRAVERSAL。

属性动画原理

动画开始后,属性动画注册监听了 Choreographer 的 vsync,使得其可以不断地调用 doAnimationFrame() 来驱动动画执行每一个关键帧。每一次的 doAnimationFrame() 调用都会去计算时间插值,而通过时间插值器计算得到 fraction 又会传给估值器,使得估值器可以计算出属性的当前值。

  • 函数调用链

  • 关键方法解析

可以看到mFrameCallback.doFrame内部是一个递归调用,不断向Choregrapher注册,不断获取回调。其中doAnimationFrame就是调用ValueAnimator.doAnimationFrame从而实现属性的重新计算和刷新。

补间动画原理

补间动画不会改变View的属性,只会改变显示效果。

简单理解就是在每一次VSYN到来时 在View的draw方法里面 根据当前时间计算动画进度,计算出一个需要变换的Transformation矩阵 然后最终设置到canvas上去 调用canvas.concat做矩阵变换。同时如果动画还未结束,会再次请求invalidate,实现动画的连续更新。

  • 函数调用链

  • 关键方法

补间动画主要还是依赖的View刷新的原理,通过请求view.invalidate,然后请求并注册下一帧的Vsync信号,等待ChoreoGrapher回调然后触发doTraversal方法执行,随后执行performMeasure+perfomLayout+performDraw 方法,再由根View往下将绘制的draw方法传递,从而实现在draw方法中实现动画的刷新。

和属性动画不同的是属性动画是独立请求的帧回调,同时消息类型是CALLBACK_ANIMATION,而View是依赖的CALLBACK_TRAVERSAL消息类型。

ViewRootImpl.java
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //Handler的同步屏障
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           //注册Vsync监听并请求发出Vsync信号
            ...
        }
}
//Vsync信号回来后执行
void doTraversal() {
        if (mTraversalScheduled) {
            ...
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            ...
            performTraversals();
            ...
        }
    }
//软件绘制
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {


        // Draw with software renderer.
        final Canvas canvas;
        ...
        canvas = mSurface.lockCanvas(dirty);
        //canvas来自于surface,也就是说应用层绘制的最终产物就是surface中的图元数据
        ...
        mView.draw(canvas);//View.draw的源头
        ...
        return true;