Android 中动画

Android动画中通常分为3种:android

1.Tween Animation:

补间动画(tweened animation)经过对场景里的对象不断作图像变换(平移、缩放、旋转)产生动画效果,便是一种渐变更画;app

缺点 :补间动画是只可以做用在View上;它只可以实现移动缩放旋转淡入淡出这四种动画操做;它只是改变了View的显示效果而已,而不会真正去改变View的属性。它并不能改变事件响应的位置,它只是单纯地修改了显示。好比说实现一个按钮的移动,那么按钮的实际点击有效区依然在原来的地方,点击移动后的地方是不会有点击事件发生的。ide

Tween Animation有四种形式:函数

  alpha            渐变透明度动画效果布局

  scale             渐变尺寸伸缩动画效果动画

  translate             画面位置移动动画效果ui

  rotate              画面旋转动画效果this

 这四种动画实现方式都是经过Animation类和AnimationUtils配合实现。lua

 

2.Frame Animation:

顺序播放事先作好的图像,是一种画面转换动画。帧动画(frame-by-frame animation)的工做原理很简单,其实就是将一个完整的动画拆分红一张张单独的图片,而后再将它们连贯起来进行播放,相似于动画片的工做原理。spa

3.Property Animation:

属性动画,经过动态地改变对象的属性从而达到动画效果,属性动画为API 11新特

新引入的属性动画机制已经再也不是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操做,同时也再也不只是一种视觉上的动画效果了。它其实是一种不断地对值进行操做的机制,并将值赋值到指定对象的指定属性上,能够是任意对象的任意属性

 

ValueAnimator

 

ValueAnimator是整个属性动画机制当中最核心的一个类,ObjectAnimator也是继承自ValueAnimator。前面咱们已经提到了,属性动画的运行机制是经过不断地对值进行操做来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,咱们只须要将初始值和结束值提供给ValueAnimator,而且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮咱们完成从初始值平滑地过渡到结束值这样的效果。除此以外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个很是重要的类。

ValueAnimator当中最经常使用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法。

那么除此以外,咱们还能够调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示从新播放和倒序播放的意思。

/*

*/

  1. ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);    
  2. anim.setDuration(300);  
  3. anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    
  4.     @Override    
  5.     public void onAnimationUpdate(ValueAnimator animation) {    
  6.         float currentValue = (float) animation.getAnimatedValue();    
  7.         Log.d("TAG", "cuurent value is " + currentValue);    
  8.     }    
  9. });    
  10. anim.start();  

 

这里咱们经过addUpdateListener()方法来添加一个动画的监听器,在动画执行的过程当中会不断地进行回调,这里用来监听数值的变化。

前面咱们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操做的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操做的。可是相比于浮点型或整型数据,对象的动画操做明显要更复杂一些,由于系统将彻底没法知道如何从初始对象过分到结束对象,所以这个时候咱们就须要实现一个本身的TypeEvaluator来告知系统如何进行过分。

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

 

  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,以下所示:

  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. }   

能够看到,PointEvaluator一样实现了TypeEvaluator接口并重写了evaluate()方法。其实evaluate()方法中的逻辑仍是很是简单的,先是将startValue和endValue强转成Point对象,而后一样根据fraction来计算当前动画的x和y的值,最后组装到一个新的Point对象当中并返回。

这样咱们就将PointEvaluator编写完成了,接下来咱们就能够很是轻松地对Point对象进行动画操做了,好比说咱们有两个Point对象,如今须要将Point1经过动画平滑过分到Point2,就能够这样写:

  1. Point point1 = new Point(0, 0);    
  2. Point point2 = new Point(300, 300);    
  3. ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);    
  4. anim.setDuration(5000);    
  5. anim.start();  

代码很简单,这里咱们先是new出了两个Point对象,并在构造函数中分别设置了它们的坐标点。而后调用ValueAnimator的ofObject()方法来构建ValueAnimator的实例,这里须要注意的是,ofObject()方法要求多传入一个TypeEvaluator参数,这里咱们只须要传入刚才定义好的PointEvaluator的实例就能够了。

Object Animator

相比于ValueAnimator,ObjectAnimator可能才是咱们最常接触到的类,由于ValueAnimator只不过是对进行了一个平滑的动画过渡,但咱们实际使用到这种功能的场景好像并很少。而ObjectAnimator则就不一样了,它是能够直接对任意对象的任意属性进行动画操做的,好比说View的alpha属性。

因为ObjectAnimator继承自ValueAnimator,说明ValueAnimator中可使用的方法在ObjectAnimator中也是能够正常使用的,它们的用法也很是相似。

 

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);    
  2. animator.setDuration(5000);    
  3. animator.start();  

第一个参数要求传入一个object对象,咱们想要对哪一个对象进行动画操做就传入什么,这里我传入了一个textview。第二个参数是想要对该对象的哪一个属性进行动画操做,因为咱们想要改变TextView的不透明度,所以这里传入"alpha"。后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,这里传入的值就表示将TextView从常规变换成全透明,再从全透明变换成常规。以后调用setDuration()方法来设置动画的时长,而后调用start()方法启动动画。

ofFloat()方法的第二个参数到底能够传哪些值呢?其实这里ObjectAnimator内部的工做机制并非直接对咱们传入的属性名进行操做的,而是会去寻找这个属性名对应的get和set方法,所以这里传入的名字其实无所谓的,只是一个指示而已。

不过,在使用ObjectAnimator的时候,有一点很是的重要,就是要操做的属性必须具备get、set方法,否则ObjectAnimator就没法起效。

经常使用的能够直接使用属性动画的属性包括:
(1)translationX和translationY:控制view从它布局容器左上角坐标偏移的位置;
(2)rotation、rotationX和rotationY:控制view围绕支点进行2D和3D旋转;
(3)scaleX和scaleY:控制view围绕着它的支点进行2D缩放;
(4)pivotX和pivotY:控制支点位置,围绕这个支点进行旋转和缩放处理。默认状况下,支点是view的中心点;
(5)x和y:控制view在它的容器中的最终位置,它是最初的左上角坐标和translationX、translationY的累计和;
(6)alpha:控制透明度,默认是1(不透明)。

若是一个属性不具备get和set方法,有两种解决的方法:一种是经过自定义一个属性类或者包装类,来间接地给这个属性增长get和set方法;第二种就是经过ValueAnimator来实现。

使用包装类的方法给一个属性增长set和get的方法,代码以下:

  1. private static class WrapperView{  
  2.     private View mTarget;  
  3.     public WrapperView(View target){  
  4.         mTarget = target;  
  5.     }  
  6.     public int getWidth(){  
  7.         return mTarget.getLayoutParams().width;  
  8.     }  
  9.   
  10.     public void setWidth(int width){  
  11.         mTarget.getLayoutParams().width = width;  
  12.         mTarget.requestFocus();  
  13.     }  
  14.   
  15. }  

使用的时候只须要操做包装类就能够间接地调用get,set方法了

 

  1. WrapperView wrapper = new WrapperView (mButton);  
  2. ObjectAnimator.ofInt(Wrapper,"width",500).setDuration(5000).start;  

ObjectAnimator中的ofObject()方法一样是对任意对象进行动画操做。

ObjectAnimator内部的工做机制是经过寻找特定属性的get和set方法,而后经过方法不断地对值进行改变,从而实现动画效果的。所以咱们就须要在MyAnimView中定义一个color属性,并提供它的get和set方法。那么接下来的问题就是怎样让setColor()方法获得调用了,毫无疑问,固然是要借助ObjectAnimator类,可是在使用ObjectAnimator以前咱们还要完成一个很是重要的工做,就是编写一个用于告知系统如何进行颜色过分的TypeEvaluator。(具体使用见Android属性动画彻底解析(中),ValueAnimator和ObjectAnimator的高级用法

组合动画AnimatorSet

独立的动画可以实现的视觉效果毕竟是至关有限的,所以将多个动画组合到一块儿播放就显得尤其重要。幸运的是,Android团队在设计属性动画的时候也充分考虑到了组合动画的功能,所以提供了一套很是丰富的API来让咱们将多个动画组合到一块儿。

实现组合动画功能主要须要借助AnimatorSet这个类,这个类提供了一个play()方法,若是咱们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括如下四个方法:

· after(Animator anim)   将现有动画插入到传入的动画以后执行

· after(long delay)   将现有动画延迟指定毫秒后执行

· before(Animator anim)   将现有动画插入到传入的动画以前执行

· with(Animator anim)   将现有动画和传入的动画同时执行

 

  1. ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);    
  2. ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);    
  3. ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);    
  4. AnimatorSet animSet = new AnimatorSet();    
  5. animSet.play(rotate).with(fadeInOut).after(moveIn);    
  6. animSet.setDuration(5000);    
  7. animSet.start();  

在属性动画中,AnimatorSet正是经过playTogether()、playSequentially()、animSet.play()、with()、before()、after()这些方法来控制多个动画的协同工做方式,从而作到对动画播放方式的精确控制。

Animator监听器

在不少时候,咱们但愿能够监听到动画的各类事件,好比动画什么时候开始,什么时候结束,而后在开始或者结束的时候去执行一些逻辑处理。这个功能是彻底能够实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,咱们只须要去实现这个AnimatorListener就能够监听动画的各类事件了。

你们已经知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,所以无论是ValueAnimator仍是ObjectAnimator都是可使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的,所以addListener()这个方法算是个通用的方法。

添加一个监听器的代码以下所示:

  1. anim.addListener(new AnimatorListener() {    
  2.     @Override    
  3.     public void onAnimationStart(Animator animation) {    
  4.     }    
  5.     
  6.     @Override    
  7.     public void onAnimationRepeat(Animator animation) {    
  8.     }    
  9.     
  10.     @Override    
  11.     public void onAnimationEnd(Animator animation) {    
  12.     }    
  13.     
  14.     @Override    
  15.     public void onAnimationCancel(Animator animation) {    
  16.     }    
  17. });  

 

能够看到,咱们须要实现接口中的四个方法,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。

可是也许不少时候咱们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口所有实现一遍就显得很是繁琐。不要紧,为此Android提供了一个适配器类,叫做AnimatorListenerAdapter,使用这个类就能够解决掉实现接口繁琐的问题了,以下所示:

 

  1. <span style="font-family:SimSun;font-size:12px;">1.anim.addListener(new AnimatorListenerAdapter() {    
  2. });</span>  

这里咱们向addListener()方法中传入这个适配器对象,因为AnimatorListenerAdapter中已经将每一个接口都实现好了,因此这里不用实现任何一个方法也不会报错。那么若是我想监听动画结束这个事件,就只须要单独重写这一个方法就能够了,以下所示:

 

  1. anim.addListener(new AnimatorListenerAdapter() {    
  2.     @Override    
  3.     public void onAnimationEnd(Animator animation) {    
  4.     }    
  5. });   

TypeEvaluator

可能在大多数状况下咱们使用属性动画的时候都不会用到TypeEvaluator,可是你们仍是应该了解一下它的用法,以防止当咱们遇到一些解决不掉的问题时可以想起来还有这样的一种解决方案。

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

能够看到,FloatEvaluator实现了TypeEvaluator接口,而后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction很是重要,这个参数用于表示动画的完成度的,咱们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,而后乘以fraction这个系数,再加上初始值,那么就获得当前动画的值了。

Interpolator

Interpolator这个东西很难进行翻译,直译过来的话是补间器的意思,它的主要做用是能够控制动画的变化速率,好比去实现一种非线性运动的动画效果。那么什么叫作非线性运动的动画效果呢?就是说动画改变的速率不是一成不变的,像加速运动以及减速运动都属于非线性运动。

TimeInterpolator接口已经有很是多的实现类了,这些都是Android系统内置好的而且咱们能够直接使用的Interpolator。每一个Interpolator都有它各自的实现效果,好比说AccelerateInterpolator就是一个加速运动的Interpolator,而DecelerateInterpolator就是一个减速运动的Interpolator。

 

  1. <span style="font-family:SimSun;font-size:12px;">anim.setInterpolator(new AccelerateInterpolator(2f));  </span>  

这里调用了setInterpolator()方法,而后传入了一个AccelerateInterpolator的实例,注意AccelerateInterpolator的构建函数能够接收一个float类型的参数,这个参数是用于控制加速度的。

编写自定义Interpolator:

最主要的难度都是在于数学计算方面的,所以这里也就写一个简单点的Interpolator来给你们演示一下。既然属性动画默认的Interpolator是先加速后减速的一种方式,这里咱们就对它进行一个简单的修改,让它变成先减速后加速的方式。

新建DecelerateAccelerateInterpolator类,让它实现TimeInterpolator接口,代码以下所示:

  1. public class DecelerateAccelerateInterpolator implements TimeInterpolator{    
  2.     
  3.     @Override    
  4.     public float getInterpolation(float input) {    
  5.         float result;    
  6.         if (input <= 0.5) {    
  7.             result = (float) (Math.sin(Math.PI * input)) / 2;    
  8.         } else {    
  9.             result = (float) (2 - Math.sin(Math.PI * input)) / 2;    
  10.         }    
  11.         return result;    
  12.     }    
  13.     
  14. }  

ViewProperty Animator

ViewPropertyAnimator其实算不上什么高级技巧,它的用法格外的简单,只不过和前面所学的全部属性动画的知识不一样,它并非在3.0系统当中引入的,而是在3.1系统当中附增的一个新的功能,所以这里咱们把它做为整个属性动画系列的收尾部分。

咱们都知道,属性动画的机制已经不是再针对于View而进行设计的了,而是一种不断地对值进行操做的机制,它能够将值赋值到指定对象的指定属性上。可是,在绝大多数状况下,我相信你们主要都仍是对View进行动画操做的。Android开发团队也是意识到了这一点,没有为View的动画操做提供一种更加便捷的用法确实是有点太不人性化了,因而在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。

那咱们先来回顾一下以前的用法吧,好比咱们想要让一个TextView从常规状态变成透明状态,就能够这样写:

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);    
  2. animator.start();    

咱们要将操做的view、属性、变化的值都一块儿传入到ObjectAnimator.ofFloat()方法当中,虽然看上去也没写几行代码,但这不太像是咱们平时使用的面向对象的思惟。

那么下面咱们就来看一下如何使用ViewPropertyAnimator来实现一样的效果,ViewPropertyAnimator提供了更加易懂、更加面向对象的API,以下所示:

  1. textview.animate().alpha(0f);   

果真很是简单!不过textview.animate()这个方法是怎么回事呢?animate()方法就是在Android 3.1系统上新增的一个方法,这个方法的返回值是一个ViewPropertyAnimator对象,也就是说拿到这个对象以后咱们就能够调用它的各类方法来实现动画效果了,这里咱们调用了alpha()方法并转入0,表示将当前的textview变成透明状态。

怎么样?比起使用ObjectAnimator,ViewPropertyAnimator的用法明显更加简单易懂吧。除此以外,ViewPropertyAnimator还能够很轻松地将多个动画组合到一块儿,好比咱们想要让textview运动到500,500这个坐标点上,就能够这样写:

  1. textview.animate().x(500).y(500);   

能够看出,ViewPropertyAnimator是支持连缀用法的,咱们想让textview移动到横坐标500这个位置上时调用了x(500)这个方法,而后让textview移动到纵坐标500这个位置上时调用了y(500)这个方法,将全部想要组合的动画经过这种连缀的方式拼接起来,这样所有动画就都会一块儿被执行。

那么怎样去设定动画的运行时长呢?很简单,也是经过连缀的方式设定便可,好比咱们想要让动画运行5秒钟,就能够这样写:

  1. textview.animate().x(500).y(500).setDuration(5000);   

除此以外,本篇文章第一部分所学的Interpolator技术咱们也能够应用在ViewPropertyAnimator上面,以下所示:

  1. textview.animate().x(500).y(500).setDuration(5000)    
  2.         .setInterpolator(new BounceInterpolator());    

用法很简单,一样也是使用连缀的方式。相信你们如今都已经体验出来了,ViewPropertyAnimator其实并无什么太多的技巧可言,用法基本都是大同小异的,须要用到什么功能就连缀一下,所以更多的用法你们只须要去查阅一下文档,看看还支持哪些功能,有哪些接口能够调用就能够了。

那么除了用法以外,关于ViewPropertyAnimator有几个细节仍是值得你们注意一下的:

· 整个ViewPropertyAnimator的功能都是创建在View类新增的animate()方法之上的,这个方法会建立并返回一个ViewPropertyAnimator的实例,以后的调用的全部方法,设置的全部属性都是经过这个实例完成的。

· 你们注意到,在使用ViewPropertyAnimator时,咱们自始至终没有调用过start()方法,这是由于新的接口中使用了隐式启动动画的功能,只要咱们将动画定义完成以后,动画就会自动启动。而且这个机制对于组合动画也一样有效,只要咱们不断地连缀新的方法,那么动画就不会马上执行,等到全部在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。固然若是不想使用这一默认机制的话,咱们也能够显式地调用start()方法来启动动画。

· ViewPropertyAnimator的全部接口都是使用连缀的语法来设计的,每一个方法的返回值都是它自身的实例,所以调用完一个方法以后能够直接连缀调用它的另外一个方法,这样把全部的功能都串接起来,咱们甚至能够仅经过一行代码就完成任意复杂度的动画功能。

相关文章
相关标签/搜索