天天看点

Android属性动画源代码解析(超详细)

对代码中几个值的解释:

mstarted是animator中一个额外用于标识播放状态的值,用来指示这个动画是否需要延时执行。

mstarteddelay指示这个动画是否已经从startdelay中开始执行。

从上面这段代码中,我们了解到一个valueanimator有它自己的状态(stopped, running, seeked),另外是否延时也影响valueanimator的执行。代码的最后调用了animationhandler.start(),看来动画就是从这里启动的。别急,我们还没初始化valueanimator呢,跟进setcurrentplaytime(0)看看。

这个函数在animation开始前,设置它的初始值。这个函数用于设置animation进度为指定时间点。playtime应该介于0到animation的总时间之间,包括animation重复执行的时候。如果animation还没有开始,那么它会等到被设置这个时间后才开始。如果animation已经运行,那么setcurrenttime()会将当前的进度设置为这个值,然后从这个点继续播放。

接下来让我们看看initanimation()

这个函数一看就觉得跟初始化动画有关。这个函数在处理动画的第一帧前就会被调用。如果startdelay不为0,这个函数就会在就会在延时结束后调用。它完成animation最终的初始化。

那么mvalues是什么呢?还记得我们在文章的开头介绍objectanimator的使用吧?还有一个ofint(t target, property property, int... values)方法没有介绍。官方文档中对这个方法的解释是:构造并返回一个在int类型的values数值之间objectanimator对象。当values只有一个值的时候,这个值就作为animator的终点值。如果有两个值的话,那么这两个值就作为开始值和结束值。如果有超过两个以上的值的话,那么这些值就作为开始值,作为animator运行的中间值,以及结束值。这些值将均匀地分配到animator的持续时间。,>

接下来让我们深入ofint(...)的内部看看。

对这个函数的解释:

target 就是将要进行动画的对象

propertyname 就是这个对象属性将要进行动画的属性名

values 一组值。随着时间的推移,动画将根据这组值进行变化。

再看看anim.setintvalues这个函数

在intpropertyvaluesholder内部

跳转到父类(propertyvaluesholder)的setintvalues

这个函数其实跟我们前面介绍到的 propertyvalueholder的构造函数相似,它就是设置动画过程中需要的值。如果只有一个值,那么这个值就假定为animator的终点值,动画的初始值会自动被推断出来,通过对象的getter方法得到。当然,如果所有值都为空,那么同样的这些值也会在动画开始的时候也会自动被填上。这套自动推断填值的机制只在propertyvaluesholder对象跟objectanimator一起使用的时候才有效,并且有一个能从propertyname自动推断出的getter方法这些条件都成立的时候才能用,不然propertyvaluesholder没有办法决定这些值是什么。

在这个方法里面,我们看见从最开始的objectanimator.ofint(target,propname,values[]),也就是我们在文章的开头使用系统提供的动画初始化函数中传入的int数组,在这里得到了具体的使用。先不关心具体的使用keyframe.ofint(...)。从这里我们就可以知道原来android sdk通过传入的int[]的长度来决定animator中每个帧(frame)的值。具体的对传入的int[]的使用可以参考文章里面对objectanimator.ofint(...)的介绍。在keyframeset.ofint这个函数的最后一句话使用了intkeyframeset的构造函数来初始化这些keyframe。

在intkeyframeset的构造函数中又调用父类keyframeset的构造函数来实现。

从这个构造函数中我们又可以了解到刚刚初始化后的keyframe数组的第一项和最后一项(也就是第一帧和最后一帧)得到了优先的待遇,作为在keyframeset中的字段,估计是为了后面计算动画开始和结束的时候方便。

小结objectvalue、propertyvalueholder、keyframeset的关系

我们绕了很久,不知道是否把你弄晕了,这里稍稍总结一下。我们就不从调用的顺序一步步分析下来了,太长了。我直接说说objectvalue、propertyvalueholder、keyframeset之间的关系。这三个类比较有特点的地方,objectanimator无疑是对的api接口,objectanimator持有propertyvaluesholder作为存储关于将要进行动画的具体对象(通常是view类型的控件)的属性和动画期间需要的值。而propertyvalueholder又使用keyframeset来保存animator从开始到结束期间关键帧的值。这下子我们就了解animator在执行期间用来存储和使用的数据结构。废话一下,从propertyvalueholder、keyframeset这个两个类的源代码来看,这三个类的api的设计挺有技巧的,他们都是通过将具有特定类型的实现作为一个大的概况性的类的内部实现,通过这个大的抽象类提供对外的api(例如,propertyvaluesholder.ofint(...)的实现)。

不知道是否把你上面你是否能清楚,反正不太影响下面对objectanimator.start()流程的分析。从上面一段的分析我们了解到valueanimator.initanimation()中的mvalue是 propertyvaluesholder类型的东西。在initanimation()里mvalues[i].init()初始化它们的估值器evaluator

mevaluator当然也可以使用objectanimator.setevaluator(...)传入;为空时,sdk根据mvaluetype为我们初始化特定类型的evaluator。这样我们的初始化就完成了。接下来,跳出initanimation()回到

setcurrentplaytime(...)

对animator三种状态stopped、running、seeked的解释

对mseekedtime、mstarttime的解释

mseekedtime 当setcurrentplaytime()被调用的时候设置。如果为负数,animator还没能定位到一个值。

mstarttime 第一次在animation.animateframe()方法调用时使用。这个时间在第二次调用animateframe()时用来确定运行时间(以及运行的分数值)

setcurrentplaytime(...)中doanimationframe(currenttime) 之前的代码其实都是对animator的初始化。看来doanimator(...)就是真正处理动画帧的函数了。这个函数主要主要用来处理animator中的一帧,并在有必要的时候调整animator的开始时间。

看来这个函数就是调整了一些参数,真正的处理函数还在animationframe(...)中。我们跟进去看看。

这个内部函数对给定的animation的一个简单的动画帧进行处理。currenttime这个参数是由定时脉冲(先不要了解这个定时脉冲是什么,后面我们会涉及)通过handler发送过来的(当然也可能是初始化的时候,被程序调用的,就像我们的分析过程一样),它用于计算animation已运行的时间,以及已经运行分数值。这个函数的返回值标识这个animation是否应该停止(在运行时间超过animation应该运行的总时长的时候,包括重复次数超过的情况)。

我们可以把这个函数里面的fraction简单地理解成animator的进度条的当前的位置。if (fraction >= 1f) 注意到函数里面的这句话,当animator开始需要重复执行的时候,那么就需要执行这个if判断里面的东西,这里面主要就是记录和改变重复执行animator的一些状态和变量。为了不让这篇文章太复杂,我们这里就不进行分析了。通过最简单的animator只执行一次的情况来分析。那么接下来就应该执行animatevalue(fraction)了。

在每一个animator的帧,这个函数都会被调用,结合传入的参数:已运行时间分数(fraction)。这个函数将已经运行的分数转为interpolaterd分数,然后转化成一个可用于动画的值(通过evaluator、这个函数通常在animation update的时候调用,但是它也可能在end()函数调用的时候被调用,用来设置property的最终值)。

在这里我们需要理清一下interpolaterd和evaluator之间的关系。

我们可以把动画的过程想象成是一部电影的播放,电影的播放中有进度条,interpolator就是用来控制电影播放频率,也就是快进快退要多少倍速。然后evaluator根据interpolator提供的值计算当前播放电影中的哪一个画面,也就是进度条要处于什么位置。

这个函数分三步:

通过interpolator计算出动画运行时间的分数

变量valueanimator中的mvalues[i].calculatevalue(fraction)(也就是 propertyvaluesholder对象数组)计算当前动画的值

mevaluator.evaluate(...)计算后,我们就返回到valueanimator.animatevalue(...)中,再回退到valueanimator.setcurrentplaytime(...)。最后回到valueanimator.start(boolean playbackwards)。终于解析完了setcurrentplaytime(...)这个函数,总结一下:这个函数主要用来初始化动画的值,当然这个初始化比我们想象中的复杂多了,它主要通过propertyvaluesholder、evaluator、interpolator来进行值的初始化。propertyvalueholder又通过keyframeset来存储需要的值。

在setcurrentplaytime(0)后,紧接着就通过notifystartlisteners()通知animation启动的消息。最后通过animationhandler.start()去执行。animationhandler是一个animationhandler类型的对象,它实现了runable接口。

mhandler.start()最终就是通过mchoreographer.发送给ui系统。这个过程比较复杂,这里不介绍。我们仅仅需要知道,动画中的一帧通过这种方式发送给ui系统后,在ui系统执行完一帧后,又会回调animationhandler.run()。那么其实这个过程就相当于,animationhandler.start()开始第一次动画的执行→ui系统执行animationhandler.run()→ui系统执行完后,回调相关函数→再执行animationhandler.run().可以理解为animationhandler.run()会一直调用自身多次(当然这是由ui系统驱动的),直至动画结束。

这个过程比较复杂,如果你感兴趣,可以关注我的下一篇博客。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转自:http://www.cnblogs.com/kissazi2/p/4249213.html

继续阅读