在之前的文章中已经讲了帧动画frame-by-frame animation和补间动画tweened animation,其实这两种动画原理好简单,都是按照预先固定的动画模式来播放的,帧动画将一张张单独的图片,然后把它们连贯起来进行播放,就形成了动画效果,补间动画则是可以对View对象进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种,不过这几种都是完全按照我们预先设置好的效果来执行,不能动态的改变,所以,在Android3.0以后就引入了属性动画PropertyAnimation来满足一些特定的需求,基本来说,属性动画的功能远远比前两种动画功能强大,因为它既可以对View对象操作,也可以对非View对象操作,所以说它的功能是非常强大的,好了,现在来详细介绍一下属性动画吧。
新引入的属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,就ok了。
既然属性动画的实现机制是通过对目标对象进行赋值并修改其“属性”来实现的,这个怎么理解呢?假如我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已,即是控件的真实位置移动了,而补间动画则是在另外一个地方绘制了一个一模一样的而已,真实位置还是在原来的地方。
好了,下面通过代码来具体看怎么实现属性动画吧。
ValueAnimator是整个属性动画机制当中最核心的一个类,前面我们已经提到了,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。
但是ValueAnimator的用法却一点都不复杂,我们先从最简单的功能看起吧,比如说想要将一个值从1平滑过渡到0,时长300毫秒,就可以这样写:
ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue(); //得到动画变化的属性值
Log.d("zxy", "" + value);
}
});
anim.start();
很简单吧,调用ValueAnimator的ofFloat()方法就可以构建出一个ValueAnimator的实例,ofFloat()方法当中允许传入多个float类型的参数,这里传入1和0就表示将值从1平滑过渡到0,然后调用ValueAnimator的setDuration()方法来设置动画运行的时长,最后调用start()方法启动动画。
用法就是这么简单,现在如果你运行一下上面的代码,动画就会执行了。可是这只是一个将值从0过渡到1的动画,因为没有设置要更新动画的对象和具体的属性信息,所以看不到任何界面效果,我们怎样才能知道这个动画是不是已经真正运行了呢?这就需要打印监听器里面的内容来看下效果:
从打印日志的值我们就可以看出,ValueAnimator确实已经在正常工作了,值在300毫秒的时间内从1平滑过渡到了0,而这个计算工作就是由ValueAnimator帮助我们完成的。另外ofFloat()方法当中是可以传入任意多个参数的,因此我们还可以构建出更加复杂的动画逻辑,比如说将一个值在5秒内从0过渡到5,再过渡到3,再过渡到10,就可以这样写:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 5f, 3f, 10f); 然后再开启动画即可。当然ValueAnimtor还有两个常用方法,即:ValueAnimator.ofInt(),ValueAnimator.ofObject(),除此之外,我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。
下面我们来看一下ValueAnimtor的完整应用,当按钮点击时更新它的透明度:
private void valueAnim(){
ValueAnimator animator = ValueAnimator.ofFloat(1f,0f,1f);
animator.setDuration(5000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
mBtnStart.setAlpha(value);//在动画更新监听器中,动画的值更新变化时设置button的透明度,可以在回调中不断更新View的多个属性,使用起来更加灵活,比如设置setTranslationX和setTranslationY可以让它斜着移动。
}
});
animator.start();//开启后button的透明度会随着值的更新而更新
}
来看看效果:
这就是在监听器中通过得到的动画值的变化来更新View的相关属性,从而有动画效果。
那么ValueAnimtor与ObjectAnimator有什么区别呢?
相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,但我们实际使用到这种功能的场景好像并不多。而ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。
不过虽说ObjectAnimator会更加常用一些,但是它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个类。那么既然是继承关系,说明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的,它们的用法也非常类似,下面直接看代码:
private void startPropertyAnimOne() {//设置透明度变化从1——>0.1——>1——>0.5——>1,意思就是该对象经历四次透明度的渐变
ObjectAnimator anim = ObjectAnimator.ofFloat(mTvText, "alpha", 1f,
0.1f, 1f, 0.5f, 1f);//第一个参数传入的是任意对象,第二个参数传入的是想要对该对象的哪个属性进行动画操作,第三个参数是一个可变数组,描述的是该动画的初始值和结束值,动画的渐变效果是由ValueAnimator内部算法机制实现过度效果
anim.setDuration(5000);//持续时间
anim.addUpdateListener(new AnimatorUpdateListener() {//添加一个动画监听器,在动画执行过程中不停的回调
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();//得到动画变化的属性值
Log.v("zxy", value+"");//从中可以看到透明度值会从1.0缓慢的变化到0.1再到1.0再到0.5再到1.0
}
});
anim.start();
}
效果如下:
private void startPropertyAnimTwo(){//设置旋转360度
ObjectAnimator anim = ObjectAnimator.ofFloat(mTvText, "rotation", 0f,360f);
anim.setDuration(5000);
anim.start();
}
再看看旋转效果:
private void startPropertyAnimThree(){//设置往x轴方向偏移然后回到原来的位置
float translationX = mTvText.getTranslationX();//得到该控件移动前的X轴方向上的坐标
ObjectAnimator anim = ObjectAnimator.ofFloat(mTvText, "translationX", translationX,-500f,translationX);//向右移动500像素,然后再移动到原来的位置
anim.setDuration(5000);
anim.start();
}
移动后效果如下:
private void startPropertyAnimFour(){//把TextView在垂直方向上放大3倍再还原
ObjectAnimator anim = ObjectAnimator.ofFloat(mTvText, "scaleY", 1f, 3f, 1f);
anim.setDuration(5000);
anim.start();
}
伸缩效果如下:
好了,上面就把一些基本变换动画简单的通过代码说明了一下,那么,这时候就有人有疑问了?在这段代码中:ObjectAnimator.ofFloat(mTvText, "alpha", 1f,0.1f, 1f);你怎么知道前面那个对象有alpha这个属性呢?那么第二个参数究竟可以传入什么参数呢?那么针对这个疑问,回答就是第二个参数可以是任意值,这是不是很疑惑呢?因为ObjectAnimator就是针对对象来设计的,并不是只针对View对象而设计的,所以第二个参数就是某个对象中的某个属性,然后该对象根据所赋的属性值来决定动画的表现效果。
那么textview对象中是不是有alpha属性这个值呢?没有,不仅textview没有这个属性,连它所有的父类也是没有这个属性的!这就奇怪了,textview当中并没有alpha这个属性,ObjectAnimator是如何进行操作的呢?怎么可以实现动画效果呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,然而TextView中并没有setAlpha和getAlpha方法啊?的却是这样,但是TextView是继承自View啊,我们通过查找View中的方法,发现了这两个方法,并且其它的rotation、translationX、等get和set方法也有,那么答案就有了,任何继承自View的类都有这些方法,通过set方法而把这个属性值设置给该对象,因此alpha属性才有所作用。
那么如果我们要使用复合动画呢?
实现复合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
好的,有了这四个方法,我们就可以实现复合动画了,比如我们想要先让这个字体伸缩后再旋转的同时改变透明度,就可以这样做:
//复合动画
private void animatorSet(){
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTvText, "alpha", 1f,
0.1f, 1f, 0.5f, 1f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTvText, "rotation", 0f,360f);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(mTvText, "scaleY", 1f, 3f, 1f);
AnimatorSet animSet = new AnimatorSet();//定义一个AnimatorSet对象
animSet.play(anim1).with(anim2).after(anim3);//anim3先执行,然后再同步执行anim1、anim2。【注意】先执行的anim3动画会执行6s,然后anim1和anim2一起也执行6s
animSet.setDuration(6000);
animSet.addListener(new AnimatorListenerAdapter() {//设置动画监听器,监听该动画的开始、停止、取消、结束等状态,我们往往会用AnimtorListener适配器类来只实现我们需要的方法
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//动画开始,do sthing...
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
//动画结束,do sthing...
}
});
animSet.start();
}
在很多时候,我们希望可以监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理。这个功能是完全可以实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。
大家已经知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都是可以使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的,因此addListener()这个方法算是个通用的方法。
好了,属性动画的基本用法就是这些了,当然属性动画也可以加入插值器动画,从而设置需要的效果,设置属性动画还有一个方法就是通过xml文件来设置,这就不多说了,当然还有核心用法了,那么就下次再说了!!!