Android 属性动画详解,属性动画基本用法

Hello,你们好,今天又来装逼了,装逼也上瘾啊,最近公司不是特别忙,我想这也就是我出来装逼的最好时机吧!额,,哈哈,进入正题。若有疑问欢迎留言,若有谬误欢迎批评指正。java

在Tween动画的讨论中,咱们提到在Android中动画能够分为三类:①帧动画②Tween(补间动画)③Property Animation(属性动画),在前面的文章中,分别对帧动画和Tween动画进行了很是详细的讨论,若是有兴趣能够去上面的连接去阅读。那么今天就来和你们一块儿讨论下Property Animation,相信经过本系列博客的讨论你将对Android中的动画有个很是详细的了解。
经过本篇博客你将学到如下内容:
①为何要引入属性动画
②属性动画的基本用法
③属性动画的监听器
④组合动画的实现
⑤属性动画的XML实现
android

一、为何要引入属性动画

首先来看为何要引入属性动画,我相信不少人跟我同样,看到属性动画,在脑海里闪现的第一个问题就是为何要引入属性动画?咱们都知道Android中已经有帧动画和补间动画了,那么为何还要引入属性动画呢?要想获得这个问题的正确答案,无疑要去谷歌的官网了,首先咱们来看看官网(官网地址)对Property Animation与补间动画的区别进行的介绍:app

补间动画只提供了对View进行增长动画的能力,因此若是你想对除View以外的其它对象作动画,你必须实现你本身的代码来达到这样的效果。另外,补间动画只能对View的几个方面进行动画的添加,例如View的缩放和旋转,而不是View的背景颜色等等。框架

补间动画的另外一个缺点是它只修改了视图绘制的地方,而不是实际View的自己。好比,你给Button定义了一个在屏幕上移动的动画,这个Button的绘制是正确的,可是这个Button实际能够点击的位置是没有改变的,因此你必须用你本身的逻辑来解决这个问题。ide

使用属性动画这些约束将彻底被解除,而且你能够对任何对象(Views and non-Views)的任何属性添加动画,而且这个对象的自己实际也是改变的。从更高层次上来讲,你能够选择你想要的属性,来给其添加动画,如颜色、位置或大小,而且你能够经过插值器或者多个动画的同步,来定义你所须要的动画。函数

然而补间动画须要较少的时间来设置,而且也须要更少的代码。若是补间动画完成了你所须要作的一切或者现有的代码就是按照你想要的方式工做的,那么你没有必要使用属性动画。针对不一样的状况有时候也许须要这两种动画进行工做才是有意义的。学习

以上三段就是官网给出的属性动画与补间动画的区别,可能看着比较费劲,其实引入属性动画主要有三点缘由:优化

①由于补间动画只能对View进行操做,而不能对一个对象的属性,如颜色等进行操做,而属性动画能够,而且属性动画的操做范围不只仅是View,它能够是任何对象。动画

②补间动画只能对View的几个方面作动画,也就是说补间动画不只把范围缩小到View,并且并非能对View的各个方面作动画,而只能是alpha(渐变)、scale(缩放)、translate(位移)、rotate(旋转)这四种动画。ui

③补间动画只是改变了View绘制的地方,而并无真正改变View自己。什么意思?假如手机屏幕上有一个View,咱们让他作补间动画向右移动20px,咱们会看到这个View向右移动了20px,而此时你会发现这个View是不能响应你的点击事件的,只有你点击原来的位置才能触发这个View的点击事件。由于这个View实际还在原来的位置,只不过补间动画将这个View绘制的地方向右移动了20px,而这个View真正的属性并无改变。

经过上面的介绍,相信你们已经明白了属性动画产生的缘由,了解了它产生的背景以后,接下来的一步就是要学习它的用法了。

二、属性动画的介绍

属性动画经常使用的有两个类分别是ValueAnimator和ObjectAnimator,它的继承关系图以下:
图片
从继承关系图中咱们能够清晰的看到ValueAnimator和AnimatorSet是继承与抽象类Animator的,而ObjectAnimator和TimeAnimator是继承自ValueAnimator的。这个继承关系图,你们要好好识记一下,后面会用到,知道这些关系后,咱们的讨论的方向就更加明确了,总共就这么几个类,逐一来看看呗。

三、ValueAnimator的基本使用

在学习ValueAnimator的基本使用以前,先来看下它的一些经常使用的方法

图片

在上面的方法中,可能你们比较陌生的就是of开头的那几个,其中ofFloat,ofInt它们接收的参数类型都是可变参,因此咱们能够传入任何数量的值;传进去的值列表,就表示动画时的变化范围;好比ofInt(3,9,6)就表示从数值3变化到数值9再变化到数值6。而ofObject接收两个参数一个TypeEvaluator类型的,另外一个是Object类型的可变参数,关于TypeEvaluator,后续的文章会有详细的讨论。而后就是ofPropertyValuesHolder多属性动画同时工做管理类,有时候咱们须要对一个对象的多个属性作动画,此时就会用到它。setFrameDelay设置多长时间刷新一帧,默认是10ms。但最终依赖系统的当前状态,通常咱们不用管。

说了这么多废话,到底怎么用,其实ValueAnimator的使用很是简单,首先来看个最基础的用法,假如咱们想建立一个值从0到1的动画,动画的时长为200毫秒,代码应该这样写:

图片

执行上面的代码就执行了一个值从0到1平滑过渡的动画,从上面的代码中能够看出它并无与任何的控件的任何属性有关系,从它的名字也能看出来它是对值作平滑过渡的,咱们怎么知道呢?很简单只须要对它作监听就能够了,咱们只须要添加以下代码:

图片

在上述代码中咱们给valueAnimator添加了一个addUpdateListener监听,在监听的回调中,回调给咱们的是当前状态的ValueAnimator 的实例,获得这个实例后经过调用getAnimatedValue就能够拿到当前的值,而后将其打印,这里有一点须要提醒你们注意的是拿到的这个值的类型是与of..后的值得类型相对应的,ofFloat拿到的就是float类型,ofInt拿到的就是int类型。

运行上述代码打印结果以下:
图片
从打印结果中能够看到valueAnimator的值在200毫秒内从0逐渐变化到了1,这些中间的过程谷歌已经帮咱们实现好了。

在上面咱们提到ofFloat(float… values)接收的参数类型是可变参,也就是说这里传递的参数的个数是没有限制,咱们也能够传递多个参数,好比

图片

上述代码就表示在200毫秒内,valueAnimator的值从0变化到3,而后再变化到1。ofInt的使用与ofFloat相似,只不过传的值的类型不一样。

到这里可能有的同窗会问,说了半天没有看到ValueAnimator作一个动画啊,那接下来就让一个View作位移动画,代码以下:

图片

在上述代码中经过对valueAnimator添加监听,拿到当前帧的值后,不断的设置ImageView的TranslatonX(该View在X轴的偏移量)值,从而让其移动。它的运行效果以下:
图片
能够看到咱们经过使用ValueAnimator实现了在3秒内在X轴方向上移动100px的效果。这个动画的操做是在ValueAnimator的监听中实现的。

小总结: ValueAnimator是计算动画过程当中变化的值,包含动画的开始值,结束值,持续时间等属性。可是这些值与咱们的控件是无关的,要想把计算出来的值应用到对象上,必须为ValueAnimator注册一个监听器,该监听器负责更新对象的属性值。在实现这个监听器的时候,能够经过getAnimatedValue()的方法来获取当前动画的值,拿到这个值后,咱们就能够随心所欲了。

四、ObjectAnimator的基本用法

相比于ValueAnimator,在开发中可能ObjectAnimator要比ValueAnimator用的多,由于ObjectAnimator能够直接操做对象的属性,而不用像ValueAnimator那么麻烦。
假如让一个ImageView作旋转的动画,代码能够这样写:

图片

从上述代码咱们能够看到ObjectAnimator与ValueAnimator的用法有点类似,又有不一样,在上述代码中objectAnimator调用了ofFloat()方法来去建立一个ObjectAnimator的实例,与ValueAnimator不一样的是,这里的ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,即进行动画的对象,在上面咱们传了一个ImageView。第二个参数是属性的名字,由于作旋转动画因此这里传的属性的名字为“rotation”。后面就是可变参数了,这里咱们传的是0,360,表示让ImageView旋转360度,而后设置时长,调用start方法。美女效果以下,啊,不是,是运行效果以下:

能够看到美女仍是不错的,啊。。不是,是运行效果仍是不错的。

假如想看到透明度渐变的效果呢,代码能够这么写:

图片

运行效果以下:
图片
在上面的代码中咱们设置里的“alpha”属性,让其在3秒内完成透明度从0到1的变化。

到这里从整体上看,属性动画的用法仍是比较简单的,确定有的童鞋会有疑问,ofFloat中的第二个参数都是能传哪些值呢?上面的代码中传了个“alpha”和”rotation”,可是究竟它能传哪些值呢?这一点从其名字中能够看出“属性”动画,无疑它是操做对象的属性的,因此它能够接收任意值,可是这里有一个前提,那就是这个属性必需要有get和set方法,什么意思呢?属性动画针对咱们传入的属性值,比方说“alpha”,它会去寻找这个属性名所对应的get和set方法,内部会经过java反射机制来调用set函数修改对象属性值。由此咱们能够推断出ImageView中确定会有对alpha属性的get和set操做,经过寻找你会发现这两个方法在ImageView的父类View中,经过寻找在View中确实找到了这两个方法以下:

图片

这也进一步验证咱们的说法,到这里咱们也知道,全部继承自View的控件均可以进行alpha变换,由于View中就有getAlpha和setAlpha方法。也许到这有的童鞋还会心有余悸心想上述说的我理解了,可是假如说我想对View的属性进行变换,不可能每次都要去View的源码里去看看它有没有get和set方法吧,这里呢,对常常用到的属性作一个小的总结:

①translationX和translationY:表示在X轴或者Y轴方向上的位移

② scaleX和scaleY:表示在X轴或者Y轴方向上的缩放

③rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D旋转。

④ pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认状况下,该支点的位置就是View对象的中心点。

⑤x和y:这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX和translationY值的累计和。

⑥ alpha:它表示View对象的alpha透明度。默认值是1(不透明),0表明彻底透明(不可见)。

固然咱们能够操做的属性远远不止这些,任何属性只要有get和set方法,咱们均可以操做。

ObjectAnimator是属性动画框架中最重要的实行类,建立一个ObjectAnimator只需经过他的静态工厂类直接返回一个ObjectAnimator对象。传的参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数,还包括属性的初始值,最终值,还能够调用setInterpolator设置曲线函数。

五、Animator监听

对于Animator的监听在上面的代码中也略有体现,咱们经过调用addUpdateListener这个方法给ValueAnimator添加了一个监听,其实从ValueAnimator的源码中能够看出它总共是有两个监听器的,监听器相关源码:

图片

这里有一点你们须要明白,你们能够回到开始咱们给出的继承关系图,从继承关系图中咱们能够看出
AnimatorSet和ValueAnimator是继承自Animator的,而ObjectAnimator是继承自ValueAnimator的。因此对于Animator类中的监听,AnimatorSet、ValueAnimator、ObjectAnimator均可以用,而ValueAnimator类中的监听,AnimatorSet中是没有的,而ObjectAnimator是继承自ValueAnimator的,因此ValueAnimator和ObjectAnimator都是能够调用的。理论说完,就上实例咱们能够这样为属性动画添加AnimatorListener 监听:

图片

能够看到AnimatorListener提供了对动画开始、动画重复、动画结束、取消动画作了监听。可是有时候咱们并不须要这么多啊,好比咱们只想监听动画的开始,假如用这种方法须要把这四个方法都重写才行,代码太冗余了,谷歌的攻城狮也是想到了这一点,给咱们提供了一个适配器AnimatorListenerAdapter,有这个类咱们就能够选择性的,根据须要添加监听了,好比咱们只须要添加动画开始时的监听,咱们能够这么作:

图片

有木有比上面简化了不少,能够看出谷歌对属性动画的优化仍是下了不少功夫的。

六、组合动画的实现

上面咱们都是对一个对象进行单一的动画,可是一个很酷的动画每每须要多个动画协同完成,谷歌也是给我提供了多种实现方式,一块儿来看看吧。

要想完成多个动画协同工做须要借助AnimatorSet这个类,这个类主要提供了三个播放方法,play(),playSequentially(),playTogether()。其中playSequentially()表示多个动画按顺序执,它主要有两种形式的参数playSequentially(Animator… items)和playSequentially(List <Animator> animator);一个是可变参数,另外一个是动画集合。
playTogether()表示几个动画同时执行,它接收的参数类型与playSequentially()一致。最后就是play方法了,play方法接收一个Animator动画实例,play(Animator anim),调用它以后会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括如下四个方法

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

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

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

  • with(Animator anim)   将现有动画和传入的动画同时执行
    好了,理论完了以后就要联系实际了,那接下来咱们来作一个这样的组合效果:让一张图片旋转出厂的同时伴随着渐变和缩放,代码能够这样写:

图片

运行效果以下
图片
能够看出它是渐变、旋转、缩放、三种动画的组合,效果还算不错。

接着咱们来看下play的用法,与上述动画相似,咱们来实现这样一个动画,让一张图片缩放旋转出厂,出厂以后让它消失,能够用play实现,代码以下:

图片

运行效果以下:

这样咱们就用play实现了一个比较不错的组合动画了。

七、xml文件实现

前面咱们在学Tween动画的时候,咱们是分两篇介绍的,一篇是xml文件配置的实现,一篇是代码的实现,上述咱们都是用代码实现的属性动画,那么怎么配置xml文件实现的?它的实现也很简单,首先须要作的就是在res下创建一个animator文件夹,而后建立一个xml文件,/res/animator/roation.xml。
在xml文件中总共有能够用三个标签,与代码实现是对应着的

  • <animator>  对应代码中的ValueAnimator

  • <objectAnimator>  对应代码中的ObjectAnimator

  • <set<  对应代码中的AnimatorSet
    那么它们均可以设置哪些属性值呢?
    animator中的属性以下:

    图片

  • android:duration:表示动画播放的时长

  • android:valueFrom:动画属性开始的值;取值范围为float,int和color,若是未指定,动画开始属性经过属性的get方法得到。颜色用6位16进制数表示(例如:#333333)

  • android:valueTo:动画结束值;取值范围同valueFrom

  • android:startOffset:取值范围为int,动画的start方法被调用后,延迟多少毫秒执行。

  • android:repeatCount:动画重复的次数,能够设置为-1或者正整数,-1表示无限循环,假如咱们设置成1,<font color=”#FF000000>表示重复执行一次,因此它总共会执行2次。

  • android:repeatMode:动画重复模式,取值为repeat和reverse;repeat表示正序重播,reverse表示倒序重播,这与前面讲的Tween动画是相似的。

  • android:valueType:表示参数值类型,取值为intType和floatType;与android:valueFrom、android:valueTo相对应。若是这里的取值为intType,那么android:valueFrom、android:valueTo的值也就要对应的是int类型的数值。float也是同样。若是若是android:valueFrom、android:valueTo的值设置为color类型的值,则不须要设置这个参数;

  • android:interpolator:设置加速器;

objectAnimator标签中的属性以下:

图片

能够看到与animator中的属性是差很少的,这里多了一个

  • android:propertyName=”string”表示要作动画的属性名字。其它的属性与animator中的一致,就再也不浪费口舌和篇幅了。

set标签中的属性以下:
set标签只有一个属性以下:

  • android:ordering=[“together” | “sequentially”],其中together表示set标签下的动画同时执行,而sequentially表示set标签下的动画逐个执行。

理论终于说完了,掌握了理论以后,就能够来看妹子了。
最后咱们以一个用xml实现的组合动画结束本篇的内容,咱们实现的效果是这样的,先让这个妹子进入到屏幕的正中央,而后让她旋转360度,再而后让她离开屏幕,离开屏幕的同时伴随着透明度的变化。先看效果:
图片
效果还算比较炫酷吧, 这也算是一个稍微复杂一点的动画了,与之对应的xml配置内容以下:

图片

怎样将其xml文件加载到程序中呢?代码也很简单,只须要这样写:

图片

能够看到,直接调用AnimatorInflater的loadAnimator将xml文件加载进来,并给其设置目标对象,最后调用start方法启动,就完成了。xml文件的配置你们能够根据运行效果本身分析分析。因为篇幅缘由以及今天是阴天的缘由,这一篇就写到这里了,由于我得赶忙去买个避雷针去。