属性动画和View动画不同,属性动画可以作用于任何对象,属性动画中有VlaueAnimator、ObjectAnimator、AnimatorSet等。
属性动画可以在一个时间间隔内完成对象从一个属性值到另一个属性值的改变,例如:
- 改变一个view的宽高
改变一个view的背景色,实现某一个时间段内的渐变效果。
ValueAnimator animator = ObjectAnimator.ofInt(this,"backgroundColor",0xFFFF8080,0XFF8080FF); animator.setDuration(3000); animator.setEvaluator(new ArgbEvaluator()); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setRepeatMode(ValueAnimator.REVERSE); animator.start();
动画集合,同时对view做几个不同的动画效果。
属性动画还可以使用xml文件进行定义,在res/animator/目录下,语法如下:
<set android:ordering="together"|"sequentially" <objectAnimator android:propertyName="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" /> <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" /> <set> ... </set> </set>
以上各标签的含义如下:
< set>标签对应AnimatorSet,< animator>标签对应ValueAnimator,< objectAnimator>标签对应ObjcetAnimator,< set>标签的android:ordering的两个可选值together和sequentially表示动画集合中的动画是同时播放还是顺序播放。
< animator>标签和< objectanimator>基本相同,只是比< objectAnimator>标签少了一个属性android:propertyName属性,其他都是一样的。
- propertyName 表示属性动画的作用对象的属性名称
- duration 表示动画的时长
- valueFrom 表示属性的起始值
- valueTo 表示属性的结束值
- startOffset 表示动画的延迟时间
- repeatCount 表示动画的重复次数,默认值是0,其中-1表示无限循环
- repeatMode 表示动画的重复模式,repeat表示连续重复,reverse表示逆向重复。
- valueType 表示propertyName所指定的属性的类型,有intType和floatType两个可选项,如果porpertyName所指定的属性值是颜色,那么这个属性不需要指定,系统会自动识别。
属性动画的使用:
AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(mContext,R.anim.xxx);
set.setTarget(textView);
set.start();
TimeInterpolator和TypeEvaluator
TimeInterpolator为时间插值器,作用是根据时间的流逝的百分比来计算出当前属性值改变的百分比,系统自带的有LinearInterpolator(匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等。TypeEvaluator为类型估值算法,系统自带的有IntEvaluator、FloatEvaluator、ArgbEvaluator三种,分别针对整数类型,浮点数类型和颜色类型。
时间插值器是我们实现非匀速动画的重要手段,如果需要,可以自定义,需要实现Interpolator或者TimeInterpolator,实现其中的getInterpolation方法。
属性动画的监听器
属性动画提供了两个监听器,用于监听动画的播放过程。
AnimatorListener
public static interface AnimatorListener{ void onAnimationStart(Animator animator); void onAnimationEnd(Animator animator); void onAnimationCancel(Animator animator); void onAnimationRepeat(Animator animator); }
AnimatorListener监听了动画的开始、结束、取消、重复四个动作。同时系统提供了AnimatorListenerAdapter,使用这个类我们可以有选择的监听我们需要的动作。
AnimatorUpdateListener
public static interface AnimatorUpdateListener{ void onAnimatorUpdate(ValueAnimator animator); }
这个接口比较特殊,他监听了动画的整个过程,动画每播放一帧,该接口就会被调用一次。
到这里,属性动画的基础知识就介绍完毕了,但是还有一些问题要说,如果你们现在就去尝试着给一些view写一些属性动画的话,就会发现有时候你写的属性动画没有效果?那就对了,属性动画还有一些额外的要求:
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的开始值和结束值,根据插值器计算的结果,多次调用set方法,从而以动画的形式完成对某个值的改变。
要想属性动画生效,必须满足两个条件:
- 必须提供set方法,如果动画的时候没有提供初始值,那么就还需要提供get方法,用于获取初始值。
- 属性的改变必须能够通过某种形式反映出来(感觉这条是废话。。。)
满足以上两个条件,属性动画基本就可以实现,为什么说基本可以实现呢,因为还有一些坑。。。
例如我们对Button的width属性做动画,就会发现还是没有效果,这是因为button虽然提供了getWidth和setWidth方法,但是你查看源码,会发现这个setWidth方法改变的并不是Button的宽度,而是设置了Button的最大宽度和最小宽度,因为Button是继承自TextView的,getWidth方法时继承自TextView的,是不是有点坑。。。那么这是我们该怎么办呢?
针对上述问题,官方给出了三个解决办法:
- 给你的对象加上get和set方法,如果你有权限的话。
- 用一个类来对原始对象进行包装,间接为其提供get和set方法。
- 采用ValueAnimator,监听动画过程,在监听的回调中自己实现属性的改变。
其中第一个就不说了,来说说2和3:
2 用一个类来包装原始对象,间接为其提供get和set方法
- 我们以刚才说的button为例,来说明这个方法的用法
@override
public void onClick(View v){
if(v==mButton){
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
}
}
private static class ViewWrapper{
private View mTarget;
public ViewWrapper(View target){
mTarget = target;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.getLayoutParms().width = width;
mTarget.requestLayout();
}
}
上述代码让button在5s内宽度增加到500px,我们使用ViewWrapper类来包装button,从而间接的为button提供了真正作用域button宽度的get和set方法。
3 采用valueAnimator,监听动画过程,自己实现属性的改变
前边我们也说过valueAnimator和objectAnimator的区别,就是少了一个propertyName属性,也就是说valueAnimator本身是不作用于任何对象的,直接使用的话是不会有任何效果的,但是我们可以监听他的值的变化,然后对应的去修改我们需要改变的对象的值,来实现我们的动画。
@override public void onClick(View v){ if(v==mButton){ ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100); valueAnimator.addUpdateListener(new AnimatorUpdateListener(){ //持有一个IntEvaluator对象,方便下面估值的时候使用 private IntEvaluator mEvaluator = new IntEvaluator(); @override public void onAnimationUpdate(ValueAnimator animator){ //获得当前动画的进度 int currentValue = (Integer)animator.getAnimatedValue(); //获得当前进度占整个动画过程的比例 float fraction = animator.getAnimatedFraction(); //调用整型估值器,通过比例计算出宽度,然后设置给button v.getLayoutParams().width = mEvaluator.evaluate(fraction,v.getWidth,500); v.requestLayout(); } }); } }
这种方法是利用valueAnimator,将一个值从1变到100,每改变一次,会回调AnimatorUpdateListener中的方法,我们在这个方法中根据当前值在总值中的比例来计算出我们需要改变的值的当前值,进而做出对应的改变,达到动画的效果。
至此,属性动画的相关内容全部介绍完毕。
参考资料:Android开发艺术探索