相关文章
Android开发之Frame动画(帧动画)
Android开发之Tween(补间动画)完全解析(上)——xml文件配置的实现
Android开发之Tween(补间动画)完全解析(下)——代码实现
欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/52011155
Hello,大家好,今天又来装逼了,装逼也上瘾啊,最近公司不是特别忙,我想这也就是我出来装逼的最好时机吧!额,,哈哈,进入正题。如有疑问欢迎留言,如有谬误欢迎批评指正。
在Tween动画的讨论中,我们提到在Android中动画可以分为三类:①帧动画②Tween(补间动画)③Property Animation(属性动画),在前面的文章中,分别对帧动画和Tween动画进行了非常详细的讨论,如果有兴趣可以去上面的链接去阅读。那么今天就来和大家一起讨论下Property Animation,相信通过本系列博客的讨论你将对Android中的动画有个非常详细的了解。
通过本篇博客你将学到以下内容:
①为什么要引入属性动画
②属性动画的基本用法
③属性动画的监听器
④组合动画的实现
⑤属性动画的XML实现
首先来看为什么要引入属性动画,我相信很多人跟我一样,看到属性动画,在脑海里闪现的第一个问题就是为什么要引入属性动画?我们都知道Android中已经有帧动画和补间动画了,那么为什么还要引入属性动画呢?要想得到这个问题的正确答案,无疑要去谷歌的官网了,首先我们来看看官网(官网地址)对Property Animation与补间动画的区别进行的介绍:
补间动画只提供了对View进行增加动画的能力,所以如果你想对除View之外的其它对象做动画,你必须实现你自己的代码来达到这样的效果。另外,补间动画只能对View的几个方面进行动画的添加,例如View的缩放和旋转,而不是View的背景颜色等等。
补间动画的另一个缺点是它只修改了视图绘制的地方,而不是实际View的本身。比如,你给Button定义了一个在屏幕上移动的动画,这个Button的绘制是正确的,但是这个Button实际可以点击的位置是没有改变的,所以你必须用你自己的逻辑来解决这个问题。
使用属性动画这些约束将完全被解除,并且你可以对任何对象(Views and non-Views)的任何属性添加动画,并且这个对象的本身实际也是改变的。从更高层次上来说,你可以选择你想要的属性,来给其添加动画,如颜色、位置或大小,并且你可以通过插值器或者多个动画的同步,来定义你所需要的动画。
然而补间动画需要较少的时间来设置,并且也需要更少的代码。如果补间动画完成了你所需要做的一切或者现有的代码就是按照你想要的方式工作的,那么你没有必要使用属性动画。针对不同的情况有时候也许需要这两种动画进行工作才是有意义的。
以上三段就是官网给出的属性动画与补间动画的区别,可能看着比较费劲,其实引入属性动画主要有三点原因:
①因为补间动画只能对View进行操作,而不能对一个对象的属性,如颜色等进行操作,而属性动画可以,并且属性动画的操作范围不仅仅是View,它可以是任何对象。
②补间动画只能对View的几个方面做动画,也就是说补间动画不仅把范围缩小到View,而且并不是能对View的各个方面做动画,而只能是alpha(渐变)、scale(缩放)、translate(位移)、rotate(旋转)这四种动画。
③补间动画只是改变了View绘制的地方,而并没有真正改变View本身。什么意思?假如手机屏幕上有一个View,我们让他做补间动画向右移动20px,我们会看到这个View向右移动了20px,而此时你会发现这个View是不能响应你的点击事件的,只有你点击原来的位置才能触发这个View的点击事件。因为这个View实际还在原来的位置,只不过补间动画将这个View绘制的地方向右移动了20px,而这个View真正的属性并没有改变。
通过上面的介绍,相信大家已经明白了属性动画产生的原因,了解了它产生的背景之后,接下来的一步就是要学习它的用法了。
属性动画常用的有两个类分别是ValueAnimator和ObjectAnimator,它的继承关系图如下:
从继承关系图中我们可以清晰的看到ValueAnimator和AnimatorSet是继承与抽象类Animator的,而ObjectAnimator和TimeAnimator是继承自ValueAnimator的。这个继承关系图,大家要好好识记一下,后面会用到,知道这些关系后,我们的讨论的方向就更加明确了,总共就这么几个类,逐一来看看呗。
在学习ValueAnimator的基本使用之前,先来看下它的一些常用的方法
//设置操作属性的一系列值float
ofFloat(float... values)
ofInt(int... values)
ofObject(TypeEvaluator evaluator,Object... values)
//用于实现组合动画等
ofPropertyValuesHolder(PropertyValuesHolder... values)
setFrameDelay(long frameDelay)
ValueAnimator setDuration(long duration)
//获取ValueAnimator在运动时,当前运动点的值
Object getAnimatedValue();
//开始动画
void start()
//设置循环次数,设置为INFINITE表示无限循环
void setRepeatCount(int value)
//设置循环模式 value取值有RESTART,REVERSE,
void setRepeatMode(int value)
// 取消动画
void cancel()
在上面的方法中,可能大家比较陌生的就是of开头的那几个,其中ofFloat,ofInt它们接收的参数类型都是可变参,所以我们可以传入任何数量的值;传进去的值列表,就表示动画时的变化范围;比如ofInt(3,9,6)就表示从数值3变化到数值9再变化到数值6。而ofObject接收两个参数一个TypeEvaluator类型的,另一个是Object类型的可变参数,关于TypeEvaluator,后续的文章会有详细的讨论。然后就是ofPropertyValuesHolder多属性动画同时工作管理类,有时候我们需要对一个对象的多个属性做动画,此时就会用到它。setFrameDelay设置多长时间刷新一帧,默认是10ms。但最终依赖系统的当前状态,一般我们不用管。
说了这么多废话,到底怎么用,其实ValueAnimator的使用非常简单,首先来看个最基础的用法,假如我们想创建一个值从0到1的动画,动画的时长为200毫秒,代码应该这样写:
ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,1);
valueAnimator.setDuration(200);
valueAnimator.start();
执行上面的代码就执行了一个值从0到1平滑过渡的动画,从上面的代码中可以看出它并没有与任何的控件的任何属性有关系,从它的名字也能看出来它是对值做平滑过渡的,我们怎么知道呢?很简单只需要对它做监听就可以了,我们只需要添加如下代码:
ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,1);
valueAnimator.setDuration(200);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value=(Float) animation.getAnimatedValue();
Log.i("MainActivity","currentValue:"+value);
}
});
valueAnimator.start();
在上述代码中我们给valueAnimator添加了一个addUpdateListener监听,在监听的回调中,回调给我们的是当前状态的ValueAnimator 的实例,得到这个实例后通过调用getAnimatedValue就可以拿到当前的值,然后将其打印,这里有一点需要提醒大家注意的是拿到的这个值的类型是与of..后的值得类型相对应的,ofFloat拿到的就是float类型,ofInt拿到的就是int类型。
运行上述代码打印结果如下:
从打印结果中可以看到valueAnimator的值在200毫秒内从0逐渐变化到了1,这些中间的过程谷歌已经帮我们实现好了。
在上面我们提到ofFloat(float… values)接收的参数类型是可变参,也就是说这里传递的参数的个数是没有限制,我们也可以传递多个参数,比如
ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,3,1);
valueAnimator.setDuration(200);
valueAnimator.start();
上述代码就表示在200毫秒内,valueAnimator的值从0变化到3,然后再变化到1。ofInt的使用与ofFloat类似,只不过传的值的类型不同。
到这里可能有的同学会问,说了半天没有看到ValueAnimator做一个动画啊,那接下来就让一个View做位移动画,代码如下:
ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,100);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value=(Float) animation.getAnimatedValue();
ivImage.setTranslationX(value);
}
});
valueAnimator.start();
在上述代码中通过对valueAnimator添加监听,拿到当前帧的值后,不断的设置ImageView的TranslatonX(该View在X轴的偏移量)值,从而让其移动。它的运行效果如下:
可以看到我们通过使用ValueAnimator实现了在3秒内在X轴方向上移动100px的效果。这个动画的操作是在ValueAnimator的监听中实现的。
小总结: ValueAnimator是计算动画过程中变化的值,包含动画的开始值,结束值,持续时间等属性。但是这些值与我们的控件是无关的,要想把计算出来的值应用到对象上,必须为ValueAnimator注册一个监听器,该监听器负责更新对象的属性值。在实现这个监听器的时候,可以通过getAnimatedValue()的方法来获取当前动画的值,拿到这个值后,我们就可以为所欲为了。
相比于ValueAnimator,在开发中可能ObjectAnimator要比ValueAnimator用的多,因为ObjectAnimator可以直接操作对象的属性,而不用像ValueAnimator那么麻烦。
假如让一个ImageView做旋转的动画,代码可以这样写:
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(ivImage,"rotation",0,360);
objectAnimator.setDuration(3000);
objectAnimator.start();
从上述代码我们可以看到ObjectAnimator与ValueAnimator的用法有点相似,又有不同,在上述代码中objectAnimator调用了ofFloat()方法来去创建一个ObjectAnimator的实例,与ValueAnimator不同的是,这里的ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,即进行动画的对象,在上面我们传了一个ImageView。第二个参数是属性的名字,因为做旋转动画所以这里传的属性的名字为“rotation”。后面就是可变参数了,这里我们传的是0,360,表示让ImageView旋转360度,然后设置时长,调用start方法。美女效果如下,啊,不是,是运行效果如下:
可以看到美女还是不错的,啊。。不是,是运行效果还是不错的。
假如想看到透明度渐变的效果呢,代码可以这么写:
ObjectAnimator objectAnimator;
objectAnimator=ObjectAnimator.ofFloat(ivImage,"alpha",0,1);
objectAnimator.setDuration(3000);
objectAnimator.start();
运行效果如下:
在上面的代码中我们设置里的“alpha”属性,让其在3秒内完成透明度从0到1的变化。
到这里从总体上看,属性动画的用法还是比较简单的,肯定有的童鞋会有疑问,ofFloat中的第二个参数都是能传哪些值呢?上面的代码中传了个“alpha”和”rotation”,但是究竟它能传哪些值呢?这一点从其名字中可以看出“属性”动画,无疑它是操作对象的属性的,所以它可以接收任意值,但是这里有一个前提,那就是这个属性必须要有get和set方法,什么意思呢?属性动画针对我们传入的属性值,比方说“alpha”,它会去寻找这个属性名所对应的get和set方法,内部会通过java反射机制来调用set函数修改对象属性值。由此我们可以推断出ImageView中肯定会有对alpha属性的get和set操作,通过寻找你会发现这两个方法在ImageView的父类View中,通过寻找在View中确实找到了这两个方法如下:
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha);
public float getAlpha();
这也进一步验证我们的说法,到这里我们也知道,所有继承自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的监听在上面的代码中也略有体现,我们通过调用addUpdateListener这个方法给ValueAnimator添加了一个监听,其实从ValueAnimator的源码中可以看出它总共是有两个监听器的,监听器相关源码:
//第一种监听:AnimatorUpdateListener 这个监听是在ValueAnimator类中的
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//添加AnimatorUpdateListener监听,系统提供给我们的方法
public void addUpdateListener(AnimatorUpdateListener listener)
//第二种监听:这个监听是在Animator这个类中的
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加AnimatorListener调用的方法
public void addListener(AnimatorListener listener);
这里有一点大家需要明白,大家可以回到开始我们给出的继承关系图,从继承关系图中我们可以看出
AnimatorSet和ValueAnimator是继承自Animator的,而ObjectAnimator是继承自ValueAnimator的。所以对于Animator类中的监听,AnimatorSet、ValueAnimator、ObjectAnimator都可以用,而ValueAnimator类中的监听,AnimatorSet中是没有的,而ObjectAnimator是继承自ValueAnimator的,所以ValueAnimator和ObjectAnimator都是可以调用的。理论说完,就上实例我们可以这样为属性动画添加AnimatorListener 监听:
objectAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
可以看到AnimatorListener提供了对动画开始、动画重复、动画结束、取消动画做了监听。但是有时候我们并不需要这么多啊,比如我们只想监听动画的开始,假如用这种方法需要把这四个方法都重写才行,代码太冗余了,谷歌的攻城狮也是想到了这一点,给我们提供了一个适配器AnimatorListenerAdapter,有这个类我们就可以选择性的,根据需要添加监听了,比如我们只需要添加动画开始时的监听,我们可以这么做:
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
有木有比上面简化了很多,可以看出谷歌对属性动画的优化还是下了很多功夫的。
上面我们都是对一个对象进行单一的动画,但是一个很酷的动画往往需要多个动画协同完成,谷歌也是给我提供了多种实现方式,一起来看看吧。
要想完成多个动画协同工作需要借助AnimatorSet这个类,这个类主要提供了三个播放方法,play(),playSequentially(),playTogether()。其中playSequentially()表示多个动画按顺序执,它主要有两种形式的参数playSequentially(Animator… items)和playSequentially(List
playTogether()表示几个动画同时执行,它接收的参数类型与playSequentially()一致。最后就是play方法了,play方法接收一个Animator动画实例,play(Animator anim),调用它之后会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法
ObjectAnimator scaleYAnimator=ObjectAnimator.ofFloat(ivImage,"scaleY",0,1);
ObjectAnimator scaleXAnimator=ObjectAnimator.ofFloat(ivImage,"scaleX",0,1);
ObjectAnimator rotationXAnimator=ObjectAnimator.ofFloat(ivImage,"rotation",0,360);
ObjectAnimator alphaAnimator=ObjectAnimator.ofFloat(ivImage,"alpha",0,1);
AnimatorSet set=new AnimatorSet();
set.playTogether(scaleXAnimator,scaleYAnimator,alphaAnimator,rotationXAnimator);
set.setDuration(3000);
set.start();
运行效果如下
可以看出它是渐变、旋转、缩放、三种动画的组合,效果还算不错。
接着我们来看下play的用法,与上述动画类似,我们来实现这样一个动画,让一张图片缩放旋转出厂,出厂之后让它消失,可以用play实现,代码如下:
ObjectAnimator scaleYAnimator=ObjectAnimator.ofFloat(ivImage,"scaleY",0,1);
ObjectAnimator scaleXAnimator=ObjectAnimator.ofFloat(ivImage,"scaleX",0,1);
ObjectAnimator rotationXAnimator=ObjectAnimator.ofFloat(ivImage,"rotation",0,360);
ObjectAnimator alphaAnimator=ObjectAnimator.ofFloat(ivImage,"alpha",1,0);
AnimatorSet set=new AnimatorSet();
/**让scaleYAnimator、scaleXAnimator、rotationXAnimator同时执行
*执行完之后执行alphaAnimator*/
set.play(scaleXAnimator).with(scaleYAnimator);
set.play(scaleYAnimator).with(rotationXAnimator);
set.play(alphaAnimator).after(rotationXAnimator);
set.setDuration(3000);
set.start();
运行效果如下:
这样我们就用play实现了一个比较不错的组合动画了。
前面我们在学Tween动画的时候,我们是分两篇介绍的,一篇是xml文件配置的实现,一篇是代码的实现,上述我们都是用代码实现的属性动画,那么怎么配置xml文件实现的?它的实现也很简单,首先需要做的就是在res下建立一个animator文件夹,然后创建一个xml文件,/res/animator/roation.xml。
在xml文件中总共有可以用三个标签,与代码实现是对应着的
animator中的属性如下:
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]
android:interpolator=["@android:interpolator/XXX"]/>
android:duration:表示动画播放的时长
android:valueFrom:动画属性开始的值;取值范围为float,int和color,如果未指定,动画开始属性通过属性的get方法获得。颜色用6位16进制数表示(例如:#333333)
android:valueTo:动画结束值;取值范围同valueFrom
android:startOffset:取值范围为int,动画的start方法被调用后,延迟多少毫秒执行。
android:repeatCount:动画重复的次数,可以设置为-1或者正整数,-1表示无限循环,假如我们设置成1,表示重复执行一次,所以它总共会执行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标签中的属性如下:
"string"
android:duration="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]
android:interpolator=["@android:interpolator/XXX"]/>
可以看到与animator中的属性是差不多的,这里多了一个
set标签中的属性如下:
set标签只有一个属性如下:
- android:ordering=[“together” | “sequentially”],其中together表示set标签下的动画同时执行,而sequentially表示set标签下的动画逐个执行。
理论终于说完了,掌握了理论之后,就可以来看妹子了。
最后我们以一个用xml实现的组合动画结束本篇的内容,我们实现的效果是这样的,先让这个妹子进入到屏幕的正中央,然后让她旋转360度,再然后让她离开屏幕,离开屏幕的同时伴随着透明度的变化。先看效果:
效果还算比较炫酷吧, 这也算是一个稍微复杂一点的动画了,与之对应的xml配置内容如下:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially" >
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="-500"
android:valueTo="0"
android:valueType="floatType" >
objectAnimator>
<set android:ordering="sequentially" >
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" >
objectAnimator>
<set android:ordering="together" >
<objectAnimator
android:duration="2000"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" >
objectAnimator>
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="0"
android:valueTo="200"
android:valueType="floatType" >
objectAnimator>
set>
set>
set>
怎样将其xml文件加载到程序中呢?代码也很简单,只需要这样写:
Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,
R.animator.animator_set);
animator.setTarget(ivImage);
animator.start();
可以看到,直接调用AnimatorInflater的loadAnimator将xml文件加载进来,并给其设置目标对象,最后调用start方法启动,就完成了。xml文件的配置大家可以根据运行效果自己分析分析。
由于篇幅原因以及今天是阴天的原因,这一篇就写到这里了,因为我得赶紧去买个避雷针去。
如果你想看我装逼,那就锁定本台期待接下来的关于属性动画的文章吧。
欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/52011155
参考文章:https://developer.android.com/guide/topics/resources/animation-resource.html