Android属性动画完全解析 中 ,ValueAnimator和ObjectAnimator的高级用法

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/43536355

大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了。但是,正如上篇文章当中所说到的,属性动画对补间动画进行了很大幅度的改进,之前补间动画可以做到的属性动画也能做到,补间动画做不到的现在属性动画也可以做到了。因此,今天我们就来学习一下属性动画的高级用法,看看如何实现一些补间动画所无法实现的功能。

阅读本篇文章需要你对属性动画有一定的了解,并且掌握属性动画的基本用法,如果你还对属性动画不够了解的话,建议先去阅读 Android属性动画完全解析(上),初识属性动画的基本用法 。

ValueAnimator的高级用法

在上篇文章中介绍补间动画缺点的时候有提到过,补间动画是只能对View对象进行动画操作的。而属性动画就不再受这个限制,它可以对任意对象进行动画操作。那么大家应该还记得在上篇文章当中我举的一个例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。OK,下面我们就来学习一下如何实现这样的效果。

在开始动手之前,我们还需要掌握另外一个知识点,就是TypeEvaluator的用法。可能在大多数情况下我们使用属性动画的时候都不会用到TypeEvaluator,但是大家还是应该了解一下它的用法,以防止当我们遇到一些解决不掉的问题时能够想起来还有这样的一种解决方案。

那么TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。我们在上一篇文章中学到的ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. public class FloatEvaluator implements TypeEvaluator {  
  2.     public Object evaluate(float fraction, Object startValue, Object endValue) {  
  3.         float startFloat = ((Number) startValue).floatValue();  
  4.         return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
  5.     }  
  6. }  
可以看到,FloatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。

好的,那FloatEvaluator是系统内置好的功能,并不需要我们自己去编写,但介绍它的实现方法是要为我们后面的功能铺路的。前面我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。

下面来先定义一个Point类,如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. public class Point {  
  2.   
  3.     private float x;  
  4.   
  5.     private float y;  
  6.   
  7.     public Point(float x, float y) {  
  8.         this.x = x;  
  9.         this.y = y;  
  10.     }  
  11.   
  12.     public float getX() {  
  13.         return x;  
  14.     }  
  15.   
  16.     public float getY() {  
  17.         return y;  
  18.     }  
  19.   
  20. }  

Point类非常简单,只有x和y两个变量用于记录坐标的位置,并提供了构造方法来设置坐标,以及get方法来获取坐标。接下来定义PointEvaluator,如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. public class PointEvaluator implements TypeEvaluator{  
  2.   
  3.     @Override  
  4.     public Object evaluate(float fraction, Object startValue, Object endValue) {  
  5.         Point startPoint = (Point) startValue;  
  6.         Point endPoint = (Point) endValue;  
  7.         float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());  
  8.         float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());  
  9.         Point point = new Point(x, y);  
  10.         return point;  
  11.     }  
  12.   
  13. }  
可以看到,PointEvaluator同样实现了TypeEvaluator接口并重写了evaluate()方法。其实evaluate()方法中的逻辑还是非常简单的,先是将startValue和endValue强转成Point对象,然后同样根据fraction来计算当前动画的x和y的值,最后组装到一个新的Point对象当中并返回。

这样我们就将PointEvaluator编写完成了,接下来我们就可以非常轻松地对Point对象进行动画操作了,比如说我们有两个Point对象,现在需要将Point1通过动画平滑过度到Point2,就可以这样写:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. Point point1 = new Point(00);  
  2. Point point2 = new Point(300300);  
  3. ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);  
  4. anim.setDuration(5000);  
  5. anim.start();  
代码很简单,这里我们先是new出了两个Point对象,并在构造函数中分别设置了它们的坐标点。然后调用ValueAnimator的ofObject()方法来构建ValueAnimator的实例,这里需要注意的是,ofObject()方法要求多传入一个TypeEvaluator参数,这里我们只需要传入刚才定义好的PointEvaluator的实例就可以了。

好的,这就是自定义TypeEvaluator的全部用法,掌握了这些知识之后,我们就可以来尝试一下如何通过对Point对象进行动画操作,从而实现整个自定义View的动画效果。

新建一个MyAnimView继承自View,代码如下所示:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. public class MyAnimView extends View {  
  2.   
  3.     public static final float RADIUS = 50f;  
  4.   
  5.     private Point currentPoint;  
  6.   
  7.     private Paint mPaint;  
  8.   
  9.     public MyAnimView(Context context, AttributeSet attrs) {  
  10.         super(context, attrs);  
  11.         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  12.         mPaint.setColor(Color.BLUE);  
  13.     }  
  14.   
  15.     @Override  
  16.     protected void onDraw(Canvas canvas) {  
  17.         if (currentPoint == null) {  
  18.             currentPoint = new Point(RADIUS, RADIUS);  
  19.             drawCircle(canvas);  
  20.             startAnimation();  
  21.         } else {  
  22.             drawCircle(canvas);  
  23.         }  
  24.     }  
  25.   
  26.     private void drawCircle(Canvas canvas) {  
  27.         float x = currentPoint.getX();  
  28.         float y = currentPoint.getY();  
  29.         canvas.drawCircle(x, y, RADIUS, mPaint);  
  30.     }  
  31.   
  32.     private void startAnimation() {  
  33.         Point startPoint = new Point(RADIUS, RADIUS);  
  34.         Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);  
  35.         ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);  
  36.         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  37.             @Override  
  38.             public void onAnimationUpdate(ValueAnimator animation) {  
  39.                 currentPoint = (Point) animation.getAnimatedValue();  
  40.                 invalidate();  
  41.             }  
  42.         });  
  43.         anim.setDuration(5000);  
  44.         anim.start();  
  45.     }  
  46.   
  47. }  
基本上还是很简单的,总共也没几行代码。首先在自定义View的构造方法当中初始化了一个Paint对象作为画笔,并将画笔颜色设置为蓝色,接着在onDraw()方法当中进行绘制。这里我们绘制的逻辑是由currentPoint这个对象控制的,如果currentPoint对象不等于空,那么就调用drawCircle()方法在currentPoint的坐标位置画出一个半径为50的圆,如果currentPoint对象是空,那么就调用startAnimation()方法来启动动画。

那么我们来观察一下startAnimation()方法中的代码,其实大家应该很熟悉了,就是对Point对象进行了一个动画操作而已。这里我们定义了一个startPoint和一个endPoint,坐标分别是View的左上角和右下角,并将动画的时长设为5秒。然后有一点需要大家注意的,就是我们通过监听器对动画的过程进行了监听,每当Point值有改变的时候都会回调onAnimationUpdate()方法。在这个方法当中,我们对currentPoint对象进行了重新赋值,并调用了invalidate()方法,这样的话onDraw()方法就会重新调用,并且由于currentPoint对象的坐标已经改变了,那么绘制的位置也会改变,于是一个平移的动画效果也就实现了。

下面我们只需要在布局文件当中引入这个自定义控件:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     >  
  5.   
  6.     <com.example.tony.myapplication.MyAnimView  
  7.         android:layout_width="match_parent"  
  8.         android:layout_height="match_parent" />  
  9.   
  10. </RelativeLayout>  

最后运行一下程序,效果如下图所示:


OK!这样我们就成功实现了通过对对象进行值操作来实现动画效果的功能,这就是ValueAnimator的高级用法。

ObjectAnimator的高级用法

ObjectAnimator的基本用法和工作原理在上一篇文章当中都已经讲解过了,相信大家都已经掌握。那么大家应该都还记得,我们在吐槽补间动画的时候有提到过,补间动画是只能实现移动、缩放、旋转和淡入淡出这四种动画操作的,功能限定死就是这些,基本上没有任何扩展性可言。比如我们想要实现对View的颜色进行动态改变,补间动画是没有办法做到的。

但是属性动画就不会受这些条条框框的限制,它的扩展性非常强,对于动态改变View的颜色这种功能是完全可是胜任的,那么下面我们就来学习一下如何实现这样的效果。

大家应该都还记得,ObjectAnimator内部的工作机制是通过寻找特定属性的get和set方法,然后通过方法不断地对值进行改变,从而实现动画效果的。因此我们就需要在MyAnimView中定义一个color属性,并提供它的get和set方法。这里我们可以将color属性设置为字符串类型,使用#RRGGBB这种格式来表示颜色值,代码如下所示:

猜你喜欢

转载自www.cnblogs.com/skiwnchhw/p/10138350.html