Android属性动画(插值器与估值器)

弓背霞明剑照霜,秋风走马出咸阳。这篇文章主要讲述Android属性动画:插值器与估值器相关的知识,希望能为你提供帮助。
声明:本篇文章部分内容来自《Android开发艺术探索》。
 
我们都知道对于属性动画可以对某个属性做动画,而  插值器(TimeInterpolator)和  估值器(TypeEvaluator)在其中扮演了重要角色,下面先了解下  TimeInterpolator  和TypeEvaluator。
 
TimeInterpolator(时间插值器):
 
作用:根据时间流逝的百分比计算出当前属性值改变的百分比。
 
系统已有的插值器: 
 

  • LinearInterpolator(线性插值器):匀速动画。 
  • AccelerateDecelerateInterpolator(加速减速插值器):动画两头慢,中间快。 
  • DecelerateInterpolator(减速插值器):动画越来越慢。
 
TypeEvaluator(类型估值算法,即估值器):
 
作用:根据当前属性改变的百分比来计算改变后的属性值。
 
系统已有的估值器: 
 
  • IntEvaluator:针对整型属性 
  • FloatEvaluator:针对浮点型属性 
  • ArgbEvaluator:针对Color属性
 
那么 TimeInterpolator和 TypeEvaluator  是怎么协同工作的呢?
 
它们是实现  非匀速动画  的重要手段。属性动画是对属性做动画,属性要实现动画,首先由TimeInterpolator(插值器)根据时间流逝的百分比计算出当前属性值改变的百分比,并且  插值器  将这个百分比返回,这个时候  插值器  的工作就完成了。
 
比如  插值器  返回的值是0.5,很显然我们要的不是0.5,而是当前属性的值,即当前属性变成了什么值,这就需要  估值器  根据当前属性改变的百分比来计算改变后的属性值,根据这个属性值,我们就可以设置当前属性的值了。
 
源码分析
上面的理论知识可能比较抽象,下面根据一个实例结合系统源码来分析一下:
  下图表示一个匀速动画,采用了线性插值器和整型估值算法,在40ms内,View的x属性实现从0到40的变换。
Android属性动画(插值器与估值器)

文章图片

 
由于动画的默认刷新率为10ms/帧,所以该动画将分为5帧进行。
 
当动画进行到第三帧的时候,(x=20,t=20ms)当时间  t=20ms  的时候,时间流逝的百分比是0.5(20/40=0.5),即时间过了一半。这个百分比不是我们最终想要的,我们关心的是x的变化,那么x应该变化多少呢?插值器和估值算法就起作用了。拿  线性插值器  来说,线性插值器是实现  匀速动画  的,先看一下线性插值器  的源码:
Android属性动画(插值器与估值器)

文章图片

很显然,线性插值器  的返回值和输入值一样(看getInterpolation方法),因此  t=20ms  时  插值器  返回的值是  0.5,这意味着  x属性的改变是0.5。这个时候  插值器  的工作已经完成了,插值器  的工作就是根据时间流逝的百分比计算出当前属性值改变的百分比。
 
我们得到了当前属性改变的百分比是0.5,即50%。下一步就是要算出x具体变为了什么值,这个时候  估值算法  就起作用了,它的作用就是根据当前属性改变的百分比来计算改变后的属性值。我们先看看系统提供的  整型估值算法  的源码:
Android属性动画(插值器与估值器)

文章图片

上述代码中的  evaluate方法  的三个参数分别表示  估值小数(fraction),开始值(startValue)和 结束值(endValue)。对于我们的这个例子就是  0.5,0和40。我们将这三个值代入求值,即:0+0.5*(40 - 0)=20。没错,这就是当  t=20ms  时  x=20  的由来。
 
其实对于  插值器和估值器  来说,除了系统提供的外,我们还可以自定义。实现方式也很简单,因为插值器和估值器都是一个接口,且内部都只有一个方法,我们只要实现接口就可以了,就可以做出很多绚丽的动画了。其中,自定义插值器  需要实现  Interpolator或者TimeInterpolator,自定义估值器  需要实现TypeEvaluator。
 
但是一般来说,插值器  使用系统的就足够了,估值器  自定义的可能会多一些,另外就是如果要对其他类型(非Int丶float丶color)做动画,必须自定义类型估值算法。
 
代码演示(一)
 
在这里我们用两种方式实现抛物线轨迹,一种是  固定时间的抛物线,一种是  固定长度的抛物线。
球按照抛物线轨迹运行1.5秒(参照鸿洋大神的抛物线效果):
 
首先分析一下实现过程: 
 
要实现抛物线的效果,水平方向200px/s,垂直方向加速度200px/s*s,你会发现我们只需要知道时间的变化,就可以求出某刻小球的水平位移(即x)和 数值位移(即y)。那么公式就是:x = 200t ; y=200t2。
 
但是还有一个问题就是,我们要同时记录两个值并进行  估值算法,所以我们可以创建一个Point类  来保存这两个值。前面我们使用过  ValueAnimator 的 ofFloat() 和 ofInt()方法,分别用于对  浮点型  和  整型  的数据进行动画操作的,但实际上  ValueAnimator  中还有一个ofObject()  方法,是用于对任意对象进行动画操作的。
 
相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的  TypeEvaluator  来告知系统如何进行过度。这里我们要过度的对象就是  Point对象。
 
首先,定义一个  Point类,如下所示:
1 /** 2* 保存小球坐标的类 3*/ 4 public class Point { 5float x; 6float y; 7 8public Point(){} 9 10public Point(float x,float y){ 11this.x = x; 12this.y = y; 13} 14 }

Point  类非常简单,x和y两个变量用于记录坐标的位置,并且提供了两个构造方法。
 
接下来  自定义估值算法,来告知系统如何实现初始对象到结束对象的过度(对于  插值器  来说,我们就使用  系统的线性插值器  即可,无须自定义):
Android属性动画(插值器与估值器)

文章图片

对于自定义估值算法,你需要指定一个泛型,这里我们操作的是Point 对象,当我们指定Point 为泛型时,evaluate方法  的返回值也是Point  ,并且startValue  和  endValue也是  Point  类型,这样就对了,在自定义估值算法中,我们就可以直接使用  Point  对象了。
 
但是你会发现一个问题就是,我们并没有用到  startValue  和  endValue,而是使用了一个写死的1.5f,因为没必要,我们的抛物线是与时间挂钩的,不用他们也是完全可以的,这样写的话,出来的效果就是:球运行1.5s的轨迹图。(下面我们还会有一个例子,它的自定义估值算法就是以startValue和endValue来计算的)。
 
布局很简单,一个  RelativeLayout  包含一个  显示小球的ImageView  以及一个  用于操控的Button。这里就不贴出了。
 
下面看  MainActivity  的代码:
Android属性动画(插值器与估值器)

文章图片


通过代码发现,我们调用  ValueAnimator的ofObject()  方法来构建  ValueAnimator  的实例,这里需要注意的是,ofObject()  方法要求多传入一个  TypeEvaluator  参数,这里我们只需要传入刚才定义好的  MyTypeEvaluator  的实例就可以了。
 
还有我们设置了  1500ms  的持续时间,这里的  1500ms和自定义估值器里面的1.5f不是一回事,也就是说我们就算设置的是2000ms的持续时间,得到的小球运行轨迹是一样的,因为自定义估值器里面的  1.5f是写死的。
 
运行效果:
Android属性动画(插值器与估值器)

文章图片

我们发现小球确实是抛物线运行,但是仔细看代码你会发现,我们并没有指定小球的落点位置,即  Point对象的终止属性,那么小球落点的位置是哪里呢?其实我们可以算出来,即x=200*1.5=300; y=200*1.5*1.5=450。但是现在我们更换一下需求,要求小球刚好落在屏幕的右边缘,而不是求具体时间小球的轨迹。
 
小球呈抛物线轨迹运行到屏幕的右边缘(自己尝试的抛物线效果):
 
我们知道  y=ax^2  也是抛物线的一个公式,但是仔细看这个公式你会发现,它不是以t为基准了,而是y与x的关系,也就是说y的变化直接与x挂钩。并且对于  y=ax^2,我们知道a越小,开口越大,所以为了抛物线效果好看,我们此次使用的  a=0.001。 
 
要实现这个效果,其实可以  不用ofObject()  方法,也就是说不用自定义估值算法,直接  使用ofFloat()  也是可以实现的,即直接使用系统的估值器。我们可以为  x属性设置一个匀速动画,然后设置监听器来实现为x和y设置属性值。
 
代码如下:
Android属性动画(插值器与估值器)

文章图片

Android属性动画(插值器与估值器)

文章图片

运行效果:
Android属性动画(插值器与估值器)

文章图片

确实,小球在碰到右边部就停止了,是我们想要的效果。
 
其实要实现抛物线效果方法应该还有很多。结合数学公式还可以做出很多效果,比如正选曲线运动等。
 
代码演示(二)
 
上面我们演示了抛物线效果,下面我们再来一个效果,这次我们要  自定义估值算法并且使用startValue和endValue参数  来做算法。
 
自定义估值算法:
Android属性动画(插值器与估值器)

文章图片

代码如下:
Android属性动画(插值器与估值器)

文章图片

理一下思路:
 
  1. 首先创建Point类用来保存坐标信息,Point对象即是我们要操作的对象  。
     
  2. 自定义估值算法  MyTypeEvaluator,并制定  泛型为Point类型,在  evaluate方法  中进行估值算法,为point对象的x和y赋值并将该对象返回。
     
  3. 在  MainActivity  中调用  ValueAnimator.ofObject()  方法获得  ValueAnimator  对象,并传入  自定义估值器对象 和 Point的初始对象与终止对象。
     
  4. 为  ValueAnimator  对象设置  AnimatorUpdateListener  监听,在  onAnimationUpdate()中通过  Point point = (Point) animation.getAnimatedValue();即可获得在估值算法中返回的Point对象,并为小球设置新的x和y值。
 
运行效果如下:
Android属性动画(插值器与估值器)

文章图片

【Android属性动画(插值器与估值器)】没错,小球从(0,0)坐标运行到了(200,300)坐标,并按我们预想的轨迹运行。

    推荐阅读