美文网首页Android
Android 补间动画(Tween Animation)运行原

Android 补间动画(Tween Animation)运行原

作者: 在岁月中远行 | 来源:发表于2022-09-24 21:49 被阅读0次

    我们都知道安卓补间动画的使用,系统为我们封装了几个基本的动画,也就是ScaleAnimation,AlphaAnimatioon,RotateAnimation,TranslateAnimation,它们都是继承了Animation类,然后实现了

    applyTransformation()方法,然后通过Transformation转换类和Matrix矩形类实现了各种各样的动画效果。

    那么补间动画执行的原理是怎么样的呢?我们从使用的角度去查看源码来分析原理。这里以RotateAnimation来分析:

    围绕自身顺时针旋转了500度,旋转了2000ms,最后效果。

    下面我们源码进行分析:

    从创建rotateAnimation到设置属性然后调用mButton.startAnimation(rotateAnimation);

    首先通过setStartTime()设置了动画的开始时间,表示开始时间应该是当前第一个动画帧调用。

    下面看第二个方法:

    这里也只是对一些变量进行赋值。

    下面又调用了setStratTime主要是针对屏幕关闭,开始时间不是我们绘制的下一帧,保持START_ON_FIRST_FRAME开始时间会导致动画在屏幕重新打开开始。(我也不太理解目前)

    下面看第三个方法

    这里也就是用|运算相当于增加一个标志位PFLAG_INVALIDATED。到目前为止还没有执行补间动画的逻辑。

    继续看最后一个方法:

    我们主要看p.invalidateChild(this,damage)方法,这个方法是ViewParent接口的一个抽象方法。

    p.invalidateChild(this,damage)这里的this就是View, 调用父容器的方法,也就是ViewGroup的invalidateChild方法。

    然后ViewGroup实现了这个接口

    在do{}while (parent !=null);循环中不断地执行 parent = parent.invalidateChildInParent(location, dirty);

    直到parent==null时候,所以它一直在寻找mPrent,而View树的最顶端的mParent就是ViewRootImpl,最后会走到ViewRootImpl的invalidateChildParent里面去

    接着调用invalidateRectOnScreen(dirty);这里面会调用scheduleTraversals()

    这里会抛一个mTraversalRunnable

    主要看mTraversalRunnable,我们找到mTraversalRunnable这个类

    这里就调用doTraversal方法。

    所以说,ScheduleTraverals()是将performTraversals放到一个Runnable里面,在Choreographer的待执行队列里面,这些待执行的runnable会在最近的一个16.6ms屏幕刷新信号到来的时候执行,而performTraversals()是View的三大操作:测量,布局,绘制的发起者。

    在Android屏幕刷新机制里,View树里面不管哪个View发起的绘制请求或者布局请求都会走到ViewRootImpl的schedTraversals()里面,然后再最新的一个屏幕刷新信号来到时,再通过ViewRootImpl的performTraversals从根布局DercorView依次遍历View树去执行测量,布局,绘制三大操作,这就是为什么要求布局不能层次太深,因为每一次的刷刷都会走到ViewRootImpl里面,然后在层层遍历到发生改变的View里去执行相应的布局和绘制操作。

    下面我们来看下真正动画执行的地方:

    这里的getAnimation()

    我们搜索mCurrentAnimation = 只有在setAnimation这里赋值了的,下面继续看

    more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); 这条语句。

    前面的a.initialize()和onAnimationStart()主要是动画的初始化和开始。下面继续看boolean more = a.getTransformation(drawingTime, t, 1f);这个语句

    1 记录动画第一帧的时间

    2 计算动画进度:(已经绘制的时间-动画开始的时间)/动画时长

    3将动画的进度控制在0~1之间

    最后把normalizedTime放在getTransformationAt里面执行,

    根据插值器计算实际的动画进度。然后调用了一个空方法,需要动画的子类重写该方法,实现动画逻辑。

    上述几个方法主要就是记录动画第一帧的时间,计算动画进度值,将动画进度控制在0~1之间,根据插值器计算动画的实际进度,最后调用了applyTransformation这个空方法执行动画的具体逻辑。

    在这个方法中根据传入的interpoltedTime计算需要动画操作的参数,然后由Tranforation和Matrix做动画操作。

    但是applyTransformation被调用的地方没有循环,那么一次View树的遍历绘制也就执行一次,它是怎么多次被回调的呢?

    这里当more为true时,就会再调用invalidate方法,层层通知ViewRootImpl再发起一次遍历请求,当下一个屏幕刷新信号的时候,再通过preforTraversals遍历View树的绘制,view的draw方法被执行,然后applyLegacyAnimation方法执行相关操作,以及getTransformation计算动画进度,applyTransformation()执行动画逻辑等。

    什么时候mMore为false呢,当动画进度>=1.0时候或者动画取消,那么就不会发起invalidate请求了。

    本篇总结

    1 当调用View.startAnimation(Animation)时,并没有立即执行动画,而是通过invalidate层层通过ViewRootImpl发起一次遍历View树的请求,在接收到下一个(16.6ms)屏幕信号刷新时才发起遍历View树的绘制操作,当View绑定有动画时,则执行applyLegacyAnimation方法处理相关动画逻辑。

    2 applyLegacyAnimation里面,先执行初始化initalize(),再通知动画开始onAnimationStart,然后通过getTransformation计算动画进度,并且它的返回值和动画是否结束决定是否继续通知ViewRootImpl发起遍历请求,如此重复步骤,并且调用applyTransformation方法执行动画的逻辑,直到动画结束。

    3 其实补间动画仅仅改变的是控件的位置,并没有改变控件本身的值,在View被draw时Parent View改变它的绘制参数,这样View的大小位置角度等虽然变化了,但是View的实际属性并没有改变。

    相关文章

      网友评论

        本文标题:Android 补间动画(Tween Animation)运行原

        本文链接:https://www.haomeiwen.com/subject/otgoortx.html