安卓的动画效果分析

来源:互联网 发布:舔女生体验知乎 编辑:程序博客网 时间:2024/05/09 10:58

要写一个美观的app,出了遵守良好的设计规范,比如material design规范以外,动画效果也是非常大的一个领域。如果动画效果得当,你的app做起来也会非常美观,但是想灵活的,省力的完成一个动画效果有时候确不是一件简单的事。现在让我们看下,andorid有哪些动画设计的类库。

第一类,逐帧动画。这类动画的原理相对简单,就是在drawable文件下建立一个animation-list属性的文件,并且将drawble文件列于这个属性下面,每个drawable文件指定固定的持续时间。  这类动画看似简单,但是实则威力巨大。因为它简简单单的利用连续的图片来形成动画效果,这种动画的质量取决于图片的质量和数量。如果对于连续性的动画这种不是特别适合,但是对于动画效果比较离散,并且单一的动画来说,这种就非常合适。这类动画是对下面一种补间动画的补充。

第二类,补间动画。 看这个名字就知道,其本质原理就是对于某个属性,或者某个对象连续的更改来产生的。这些属性比如说 alpha值,Scale值,Rotate旋转,Translate转移。

具体应用方面包AlphaAnimation、 TranslateAnimation ScaleAnimation  RotateAnimation 四个子类。通过构造函数的方法来初始化子类,然后可以利用set方法设置属性。然后统一通过star方法来开始动画。如果想同时实现集中动画效果,

  1. //动画集  
  2. AnimationSet set = new AnimationSet(true);  
  3. set.addAnimation(translateAnimation);  
  4. set.addAnimation(alphaAnimation);  
  5.   
  6. //设置动画时间 (作用到每个动画)  
  7. set.setDuration(1000);  
  8. this.startAnimation(set);  

答案就是AnimationSet动画集。
除了通过代码区给veiw设置动画属性外,还可以通过xml文件来设置,看下面几个例子:

ScaleAnimation

scale_anim.xml:

[xhtml] view plain copy
 print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <scale  
  4.         android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  5.         android:fromXScale="0.0"  
  6.         android:toXScale="1.0"  
  7.         android:fromYScale="0.0"  
  8.         android:toYScale="1.0"  
  9.         android:pivotX="50%"  
  10.         android:pivotY="50%"  
  11.         android:fillAfter="false"  
  12.         android:duration="500"  
  13.     />     
  14. </set>  

那么如果载入这个动画效果呢,答案就是其实也很简单,此时需要用到AnimationUtils类。 通过该类中 loadAnimation 方法来加载这些布局文件。


上面就是两种普遍的动画效果,但是在实际的自定义view的动画中,我们其实是通过重写draw方法来绘制动画的。绘制draw的时候就涉及一个问题,因为动画效果多是根据连续的时间来连续的draw,那么你当然可以利用handler的postdelay方法,通过发送消息,来刷新UI。但是这种方法更加细化了,一般需要写很多代码,并且很容易写的比较乱。当然这种方法其实是非常灵活的。基本上大量的动画效果都可以利用这种方法写出来。但是我下面要介绍一种更加轻量级的写法。(下面内容摘自郭大神的博客)

ValueAnimator

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

但是ValueAnimator的用法却一点都不复杂,我们先从最简单的功能看起吧,比如说想要将一个值从0平滑过渡到1,时长300毫秒,就可以这样写:

[java] view plaincopy
  1. ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
  2. anim.setDuration(300);  
  3. anim.start();  
怎么样?很简单吧,调用ValueAnimator的ofFloat()方法就可以构建出一个ValueAnimator的实例,ofFloat()方法当中允许传入多个float类型的参数,这里传入0和1就表示将值从0平滑过渡到1,然后调用ValueAnimator的setDuration()方法来设置动画运行的时长,最后调用start()方法启动动画。

用法就是这么简单,现在如果你运行一下上面的代码,动画就会执行了。可是这只是一个将值从0过渡到1的动画,又看不到任何界面效果,我们怎样才能知道这个动画是不是已经真正运行了呢?这就需要借助监听器来实现了,如下所示:

[java] view plaincopy
  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确实已经在正常工作了,值在300毫秒的时间内从0平滑过渡到了1,而这个计算工作就是由ValueAnimator帮助我们完成的。另外ofFloat()方法当中是可以传入任意多个参数的,因此我们还可以构建出更加复杂的动画逻辑,比如说将一个值在5秒内从0过渡到5,再过渡到3,再过渡到10,就可以这样写:

[java] view plaincopy
  1. ValueAnimator anim = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);  
  2. anim.setDuration(5000);  
  3. anim.start();  
当然也许你并不需要小数位数的动画过渡,可能你只是希望将一个整数值从0平滑地过渡到100,那么也很简单,只需要调用ValueAnimator的ofInt()方法就可以了,如下所示:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ValueAnimator anim = ValueAnimator.ofInt(0100);  
ValueAnimator当中最常用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法,我会在下篇文章进行讲解。

那么除此之外,我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。这些方法都很简单,我就不再进行详细讲解了。

ObjectAnimator

相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,但我们实际使用到这种功能的场景好像并不多。而ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。

不过虽说ObjectAnimator会更加常用一些,但是它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个类。那么既然是继承关系,说明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的,它们的用法也非常类似,这里如果我们想要将一个TextView在5秒中内从常规变换成全透明,再从全透明变换成常规,就可以这样写:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
  2. animator.setDuration(5000);  
  3. animator.start();  
可以看到,我们还是调用了ofFloat()方法来去创建一个ObjectAnimator的实例,只不过ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,我们想要对哪个对象进行动画操作就传入什么,这里我传入了一个textview。第二个参数是想要对该对象的哪个属性进行动画操作,由于我们想要改变TextView的不透明度,因此这里传入"alpha"。后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,这里传入的值就表示将TextView从常规变换成全透明,再从全透明变换成常规。之后调用setDuration()方法来设置动画的时长,然后调用start()方法启动动画,效果如下图所示:


学会了这一个用法之后,其它的用法我们就可以举一反三了,那比如说我们想要将TextView进行一次360度的旋转,就可以这样写:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
  2. animator.setDuration(5000);  
  3. animator.start();  
可以看到,这里我们将第二个参数改成了"rotation",然后将动画的初始值和结束值分别设置成0和360,现在运行一下代码,效果如下图所示:


那么如果想要将TextView先向左移出屏幕,然后再移动回来,就可以这样写:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. float curTranslationX = textview.getTranslationX();  
  2. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);  
  3. animator.setDuration(5000);  
  4. animator.start();  
这里我们先是调用了TextView的getTranslationX()方法来获取到当前TextView的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统TextView应该怎么移动,现在运行一下代码,效果如下图所示:


然后我们还可以TextView进行缩放操作,比如说将TextView在垂直方向上放大3倍再还原,就可以这样写:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
  2. animator.setDuration(5000);  
  3. animator.start();  
这里将ofFloat()方法的第二个参数改成了"scaleY",表示在垂直方向上进行缩放,现在重新运行一下程序,效果如下图所示:


到目前为止,ObjectAnimator的用法还算是相当简单吧,但是我相信肯定会有不少朋友现在心里都有同样一个疑问,就是ofFloat()方法的第二个参数到底可以传哪些值呢?目前我们使用过了alpha、rotation、translationX和scaleY这几个值,分别可以完成淡入淡出、旋转、水平移动、垂直缩放这几种动画,那么还有哪些值是可以使用的呢?其实这个问题的答案非常玄乎,就是我们可以传入任意的值到ofFloat()方法的第二个参数当中。任意的值?相信这很出乎大家的意料吧,但事实就是如此。因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来。

那么比如说我们调用下面这样一段代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);  
其实这段代码的意思就是ObjectAnimator会帮我们不断地改变textview对象中alpha属性的值,从1f变化到0f。然后textview对象需要根据alpha属性值的改变来不断刷新界面的显示,从而让用户可以看出淡入淡出的动画效果。

那么textview对象中是不是有alpha属性这个值呢?没有,不仅textview没有这个属性,连它所有的父类也是没有这个属性的!这就奇怪了,textview当中并没有alpha这个属性,ObjectAnimator是如何进行操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setAlpha(float value);  
  2. public float getAlpha();  
那么textview对象中是否有这两个方法呢?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都可以的。

既然alpha是这个样子,相信大家一定已经明白了,前面我们所用的所有属性都是这个工作原理,那么View当中一定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,不信的话你可以到View当中去找一下。

组合动画

独立的动画能够实现的视觉效果毕竟是相当有限的,因此将多个动画组合到一起播放就显得尤为重要。幸运的是,Android团队在设计属性动画的时候也充分考虑到了组合动画的功能,因此提供了一套非常丰富的API来让我们将多个动画组合到一起。

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

  • after(Animator anim)   将现有动画插入到传入的动画之后执行
  • after(long delay)   将现有动画延迟指定毫秒后执行
  • before(Animator anim)   将现有动画插入到传入的动画之前执行
  • with(Animator anim)   将现有动画和传入的动画同时执行

好的,有了这四个方法,我们就可以完成组合动画的逻辑了,那么比如说我们想要让TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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();  
可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。运行一下上述代码,效果如下图所示:


Animator监听器

在很多时候,我们希望可以监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理。这个功能是完全可以实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。

大家已经知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都是可以使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的,因此addListener()这个方法算是个通用的方法。

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. anim.addListener(new AnimatorListenerAdapter() {  
  2. });  
这里我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,如下所示:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. anim.addListener(new AnimatorListenerAdapter() {  
  2.     @Override  
  3.     public void onAnimationEnd(Animator animation) {  
  4.     }  
  5. });  

0 0
原创粉丝点击