首先为什么要3.0引入属性动画呢?明显是因为传统的补间动画,已经远远满足不了咱们的开发需求了。
在为大家介绍属性动画之前,先讲一下补间动画的缺陷:
因为补间动画的机制就是使用硬编码的方式实现的,其扩展性极低。总分为以下几点:
1.补间动画只能作用于View上
2.补间动画改变的只是View 的显示效果而已(位移动画后,View的位置并没发生变化,View的单击事件还在原来的位置)
3.补间动画只能对View进行位移、缩放、旋转、淡入淡出这四种动画操作
也正是因为这些原因,Android开发团队决定在3.0版本当中引入属性动画这个功能,那么属性动画是不是就把上述的问题全部解决掉了?下面我们就来一起看一看。
新引入的属性动画已经不单单是对View的一些操作了。实际上属性动画就是通过不断的对值进行操作的机制,并将值赋值到对象的属性上。可以是任意对象的任意属性,当然我们还可以对View进行位移、缩放等动画操作,但是同时也可以对自定义View的Point对象进行动画操作。我们只需要简单的定义动画的运行时长、初始、结束值,什么类型的动画,剩下的就交给系统负责就行了。
看到这,相信大家对属性动画,已经有了简单的了解,那么接下来咱们就来看看属性动画的庐山真面目吧!
ValueAnimator
ValueAnimator 是属性动画机制中最核心的类。主要负责初始值和结束值之间的动画过渡的计算。它内部使用一种时间循环的机制来计算值与值之间的动画过渡,在使用的时候,我们只需要将初始值和结束值提供给ValueAnimator ,并且告诉他动画所需运行的时长,那么ValueAnimator就会自动完成从初始值到结束值的平滑过渡,从而实现这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
ValueAnimator 的用法:
ValueAnimator anim = ValueAnimator.ofFloat(0f,1f); anim.setDuration(300); //监听动画执行的进度 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (float) animation.getAnimatedValue(); Log.d("TAG", "cuurent value is " + currentValue); } }); anim.start();
看下log:
ValueAnimator里面用到最多的应该是 ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法,我会在下篇文章里面进行讲解。
另外我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放。
ObjectAnimator
相对于ValueAnimator,ObjectAnimator可能才是我们接触次数最多的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,使用场景好像不是很多,而ObjectAnimator是直接对任意对象的任意属性进行动画操作的。比如View的alpha属性。
ObjectAnimator其实继承自ValueAnimator,底层的动画实现机制也是基于ValueAnimator来完成的,因为ValueAnimator仍然是整个属性动画中最核心的一个类。既然是继承关系,那么ValueAnimator的方法在ObjectAnimator中也是可以正常使用的,他们的用法也非常类似,这里如果我们想要将一个View在6秒钟从常规变换成全透明,在从全透明变成常规的,就可以这么写:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"alpha",1f,0f,1f); objectAnimator.setDuration(6000); objectAnimator.start();
第一个参数是view对象,第二个参数我传入了"alpha"是因为要改变view的不透明度。
学到这里,我们就可以举一反三了,比如将view进行一次360度的旋转:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"rotation",0f,360f); objectAnimator.setDuration(6000); objectAnimator.start();
可以看到,这里我们将第二个参数改成了"rotation"
比如将view左移出屏幕,在移动回来:
float curTranslationX = text.getTranslationX(); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"translationX",curTranslationX,-500f,curTranslationX); objectAnimator.setDuration(6000); objectAnimator.start();
这里我们先是调用了getTranslationX()方法来获取到当前view的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统view应该怎么移动
比如缩放操作,放大5倍在还原:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"scaleY",1f,3f,1f); objectAnimator.setDuration(6000); objectAnimator.start();
这里将ofFloat()方法的第二个参数改成了"scaleY"
到目前为止,相信不少朋友心里会有一个疑问,究竟ofFloat()中的第二个参数都能传哪些值呢?其实是任意的值。因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来
比如这段代码:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"alpha",1f,0f,1f);
textview对象中有没有这个alpha属性这个值呢?没有,不仅textview没有这个值,连他的父类也是没有这个属性的。那么ObjectAnimator是怎么操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是去寻找这个属性对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:
- public void setAlpha(float value);
- public float getAlpha();
那么textview对象中有没有这两个方法?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都是可以的。
既然alpha是这样,相信大家看到这里已经明白了吧,前面我们所用的所有属性都是这个原理,那么View当中一定也存在setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法的。大家有时间可以探究一下。
组合动画
独立的动画实现的视觉效果也是有限的,因此将多个动画组合到一起就显得尤为重要。Android团队也提供了一套非常丰富的API来将多个动画组合到一起。AnimatorSet
这个类提供了一个play()方法,向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimato)将会返回一个AnimatorSet.Builder的实例,包括四个方法:
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
ObjectAnimator moveIn = ObjectAnimator.ofFloat(text, "translationX", -500f, 0f); ObjectAnimator rotate = ObjectAnimator.ofFloat(text, "rotation", 0f, 360f); ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f, 1f); AnimatorSet set = new AnimatorSet(); set.play(rotate).with(fadeInOut).after(moveIn); set.setDuration(5000); set.start();先把三个动画的对象全部创建出来,然后new一个 AnimatorSet对象,然后对这三个动画对象进行排序,让旋转和淡入淡出动画同时进行,并把它们插入到平移动画的后面,最后设置动画时长以及启动动画。
Animator监听器
实现AnimatorListener去监听动画的各种事件。
大家知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都是可以使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的。所以addListener()这个方法是个通用的方法。
添加一个监听器:
set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } });
onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。
也许有时候我们并不需要监听那么多个事件,只监听动画结束的时候,AnimatorListenerAdapter,使用这个类就可以解决实现接口繁琐的问题了:
set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); } });
可以手动选择你需要监听的事件。
使用XML编写动画
如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:
- <animator> 对应代码中的ValueAnimator
- <objectAnimator> 对应代码中的ObjectAnimator
- <set> 对应代码中的AnimatorSet
那么比如说我们想要实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:
- <animator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="100"
- android:valueType="intType"/>
- <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType"
- android:propertyName="alpha"/>
另外,我们也可以使用XML来完成复杂的组合动画操作,比如将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
- <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="together" >
- <objectAnimator
- android:duration="3000"
- android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="360"
- android:valueType="floatType" >
- </objectAnimator>
- <set android:ordering="sequentially" >
- <objectAnimator
- android:duration="1500"
- android:propertyName="alpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType" >
- </objectAnimator>
- <objectAnimator
- android:duration="1500"
- android:propertyName="alpha"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" >
- </objectAnimator>
- </set>
- </set>
- </set>
最后XML文件是编写好了,那么我们如何在代码中把文件加载进来并将动画启动呢?只需调用如下代码即可:
- Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
- animator.setTarget(view);
- animator.start();