3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:property animation,这三种动画模式在SDK中被称为property animation,view animation,drawable animation。
1.View Animation(Tween Animation)
View Animation(Tween Animation):补间动画,给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。
View animation只能应用于View对象,并只改变View对象绘制的位置,而没有改变View对象本身。它只支持一部分属性,如缩放旋转移动透明度,不会变背景颜色。可以代码定义,也可以用XML定义。可以给一个View同时设置多个动画,
也可以在一个完成之后开始另一个。
用XML定义的动画放在/res/anim/文件夹内,XML文件的根元素可以为<alpha>,<scale>,<translate>,<rotate>,interpolator元素或<set>(表示以上几个动画的集合,set可以嵌套)。
默认情况下,所有动画是同时进行的。
startOffset属性:设置各个动画的开始开始时间来达到动画顺序播放的效果。
定义好动画的XML文件后,可以通过类似下面的代码对指定View应用动画。
ImageView iv = (ImageView)findViewById(R.id.iv);
Animation anim =AnimationUtils.loadAnimation(this, R.anim.anim_scale);
iv.startAnimation(anim);
2.Drawable Animation(Frame Animation)
Drawable Animation(Frame Animation):帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果。在XML中的定义方式如下:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
<item android:drawable="@drawable/iv1" android:duration="200" />
<item android:drawable="@drawable/iv2" android:duration="200" />
<item android:drawable="@drawable/iv3" android:duration="200" />
</animation-list>
示例:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
iv = (ImageView) findViewById(R.id.iv);
iv.setBackgroundResource(R.drawable.drawable_anim);
anim = (AnimationDrawable) iv.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
anim.stop();
anim.start();
return true;
}
return super.onTouchEvent(event);
}
SDK里讲,AnimationDrawable还没有完全跟Window相关联,如果想要界面显示时就开始动画的话,可以在onWindowFoucsChanged()中调用start()方法。
3.Property Animation
Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。在Property Animation中,可以对动画应用以下属性:
Duration:动画的持续时间。
TimeInterpolation:属性值的计算方式,如先快后慢。
TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值。
Repeat Country and behavior:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放。
Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移。
Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响。
3.1Property Animation的工作方式
ValueAnimator即表示一个动画,包含动画的开始值x,结束值x,持续时间t等属性。
ValueAnimator里封装了一个TimeInterpolator,TimeInterpolator定义了属性值在开始值与结束值之间的插值方法。
ValueAnimator里封装了一个TypeAnimator,根据开始、结束值与TimeIniterpolator计算得到的值计算出属性值(pixel)
,
时间的百分比:input
动画已进行的时间:Math.cos((input+1)*Math.PI)
动画总时间:2.0f
时间因子:Math.cos((input+1)*Math.PI)/2.0f
插值因子:fraction = Math.cos((input+1)*Math.PI)/2.0f +0.5f (TimeInterpolator时间插值因子)
属性值(pixel)的式求值程序(Type Evaluator)的计算方法是:
public Float evalurate(float fraction, Number startValue, Number endValue){
float startFloat = startValue.floatValue();
float endFloat = endValue.floatValue();
return startFloat + fraction(endFloat - startFloat);
}
参数分别为上一步的差值因子,开始值(pixel)与结束值(pixel)。
3.2 ValueAnimator
ValueAnimator包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。应用Property Animation有两个步聚:
- 计算属性值
- 根据属性值执行相应的动作,如改变对象的某一属性。
ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Log.i("update", ((Float) animation.getAnimatedValue()).toString());
//通过监听这个事件在属性值更新时执行相应操作,对于ValueAnimator一般要监听此事件执行相应的动作。
}
});
animator.setInterpolator(new CycleInterpolator(2));
animation.start();
Animator.AnimatorListener
onAnimationStart() onAnimationEnd() onAnimationRepeat() onAnimationCancel
可以继承AnimatorListenerAdapter而不是实现AnimatorListener接口来简化操作,这个类对AnimatorListener中的函数都定义了一个空函数体,这样我们就定义想监听的事件,只定义一空函数体。不用实现每个函数。
以下是把一个TextView的透明度在1秒内从0变至1。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f);
animator.setDuration(1000);
animator.addListener(new AnimatorListenerAdapter(){
public void on AnimationEnd(Animator animation){
Log.i("Animation","end");
}
});
oa.start();
3.3 ObjectAnimator
继承自ValueAnimator,要指定一个对象及该对象的一个属性,完成了Property Animation的全部两步操作。实际应用中一般都会用ObjectAnimator来改变某一对象的某一属性,但用ObjectAnimator有一定的限制,要想使用ObjectAnimator,应该满足以下条件:
- 对象应该有一个setter函数:set<PropertyName>(驼峰命名法)
- 如上面的例子中,像ofFloat之类的工场方法,第一个参数为对象名,第二个为属性名,后面的参数为可变参数,如果values…参数只设置了一个值的话,那么会假定为目的值,属性值的变化范围为当前值到目的值,为了获得当前值,该对象要有相应属性的getter方法:get<PropertyName>
- 如果有getter方法,其应返回值类型应与相应的setter方法的参数类型一致。
如果上述条件不满足,则不能用ObjectAnimator,应用ValueAnimator代替。
3.4 通过AnimationSet应用多个动画
AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等。
以下例子同时应用5个动画:
1、播放anim1
2、同时播放 anim2 anim3 anim4
3、播放anim5
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(anim1).before(anim2);
bouncer.play(anim2).with(anim3);
bouncer.play(anim2).with(anim4)
bouncer.play(anim5).after(amin2);
animatorSet.start();
3.5 TypeEvalutors
根据属性的开始值(pixel)、结束值(pixel)与TimeInterpolation计算出的因子计算出当前时间的属性值,android提供了以下几个evalutor(求值程序):
IntEvaluator:属性的值类型为int;
FloatEvaluator:属性的值类型为float;
ArgbEvaluator:属性的值类型为十六进制颜色值;
TypeEvaluator:一个接口,可以通过实现该接口自定义Evaluator。
自定义TypeEvalutor很简单,只需要实现一个方法,如FloatEvalutor的定义:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
float endFloat = ((Number)startValue).floatValue();
return startFloat + fraction * (endFloat - startFloat);
}
}
根据动画执行的时间跟应用的Interpolator,会计算出一个0~1之间的因子,即evalute函数中的fraction参数。
3.6 TimeInterpolator
time interpolator定义了属性值变化的方式,如线性均匀改变,先慢后渐快等。在Property Animation中是TimeInterpolator,在View Animation中是Interpolator,这两个是一样的,在3.0之前只有Interplator,3.0之后实现代码转移至了TimeInterplator。Interpolator继承自TimeInterpolator,内部没有任何其他代码。
AccelerateInterpolator 加速
DecelerateInterpolator 减速
AccelerateDecelerateInterolator 先加速后减速
AnticipateInterpolator 反向加速
AnticipateOvershootInterpolator 反向加超越,先向相反方向改变,再加速播放超出目的值,后缓慢移动至目的值。
BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
CycleInterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)
LinearInterpolator 线性
OvershottInterpolator 超越,超出目的值的播放速度后缓慢改变到目的值
TimeInterpolator 一个接口,允许你自定义interpolator,以上几个都是实现了这个接口
3.7 当Layout改变时应用动画
ViewGroup中的子元素可以通过setVisibility使其Visible、Invisible或Gone,当有子元素的可见性改变时,可以向其应用动画,通过LayoutTransition类应用此类动画:
transitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisappearingAnim);
通过setAnimator应用动画,第一个参数表示应用的情境,可以以下4种类型:
- APPEARING 当一个元素变为Visible时对其应用的动画
- CHANGE_APPEARING 当一个元素变为Visible时,因系统要重新布局有一些元素,则需要移动的元素应用的动画
- DISAPPEARING 当一个元素变为InVisible时对其应用的动画
- CHANGE_DISAPPEARING 当一个元素变为Gone时,因系统要重新布局有一些元素,则需要移动的元素应用的动画 disappearing from the container.
第二个参数为一Animator。
transitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 10);
此函数设置动画持续时间,参数分别为类型与时间。
3.8 Keyframes
keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态,而且在两个keyFrame之间可以定义不同的Interpolator,就相当多个动画的拼接,第一个动画的结束点是第二个动画的开始点。KeyFrame是抽象类,要通过ofInt(),ofFloat(),ofObject()获得适当的KeyFrame,然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象,如以下例子:
Keyframe kf0 = Keyframe.ofInt(0, 400);
Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
Keyframe kf3 = Keyframe.ofInt(0.75f, 100);
Keyframe kf4 = Keyframe.ofInt(1f, 500);
PropertyValuesHolder propertyValues = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf3, kf4);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(btn, pvhRotation);
objectAnimator.setDuration(2000);
objectAnimator.start();
上述代码的意思是:设置btn对象的width属性值随时间的变化而变化,先后是400,200,400,100,500。
第一个参数是时间百分比,第二个参数是该时间对应的属性值。
定义了一些Keyframe后,通过PropertyValuesHolder类的方法ofKeyframe封装,然后通过ObjectAnimator.ofPropertyValuesHolder获得Animator。
下面的代码可以实现同样的效果:
ObjectAnimator oa=ObjectAnimator.ofInt(btn2, "width", 400,200,400,100,500);
oa.setDuration(2000);
oa.start();
3.9 Animating Views
在View Animation中,对View应用Animation并没有改变View的属性,动画的实现是通过其Parent View实现的,在View被drawn时Parents View改变它的绘制参数,draw后改变参数无效,这样虽然View的大小或旋转角度等改变了,但view的实际属性没变,所以有效区还是应用动画之前的区域。为了改变这一点,在Android3.0中给View增加了一些参数并对这些参数怎机啊了相应的getter/setter函数(ObjectAnimator要用这些函数改变这些属性):
translationX,translationY:转换坐标(control where the View is Located as a delta from its left androi top coordinates which are set by its layout container.)
rotation,rotationX,rotationY:旋转,rotation用于2D旋转角度,3D中用到后两个。
scaleX,scaleY:缩放
x,y:View的最终坐标
alpha:透明度
跟位置有关的参数有3个,以X坐标为例,可以通过getLeft(),getX(),getTranslateX()获得,若有一个Button按钮,布局时坐标为(30,0):
Button btn = (Button) findViewById(R.id.btn);
btn.getLeft();//30
btn.getX();//30
btn.getTranslationX();//0
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btn, "translationX", 200);
objectAnimator.setDuration(1000);
objectAnimator.start();
btn.getLeft();//30
btn.getX();//230
btn.getTranslationX();//200
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(btn,"x",200);
objectAnimator2.setDuration(1000);
objectAnimator2.start();
btn.getLeft();//30
btn.getX();//200
btn.getTranslationX();//170
无论怎样应用动画,原来的布局时的位置通过getLeft()获得,保持不变;
X是View最终的位置;
对于X动画,源代码是这样的:
case X:
info.mTranslationX = value - mView.mLeft;
break;
Property Animation也可以在XML中定义
- <set> - AnimatorSet
- <animator> - ValueAnimator
- <objectAnimator> - ObjectAnimator
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);
animatorSet.setTarget(myObject);
animatorSet.start();
3.10 ViewPropertyAnimator
如果需要对一个View的多个属性进行动画可以用ViewPropertyAnimator类,该类对多属性动画进行了优化,会合并一些invalidate()来减少刷新视图,该类在3.1中引入。
以下两段代码实现同样的效果:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);