android property aniamtion(属性动画)详解
本文将围绕属性动画的几个类(ValueAnimator,ObjectAnimator,AnimatorSet,PropertyValuesHolder 等等)详细介绍属性动的用法和原理,以及属性动画在xml资源的定义。
1.Animator
属性动画的基类,他是一个抽象类,只是定义了跟动画相关的规则,没有什么具体实现。我们都不会直接用到它。
2.ValueAnimator
Animator的子类,从类名就可猜到它的作用是某些“值”的变化,先来看看一个例子
//ofFloat初始化,0f 和 100f是动画的初始值和结束值
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
//设置动画时间
animation.setDuration(1000);
//设置监听,每隔一段时间(默认是10 ms,不同手机会有所不同),调一次onAnimationUpdate()方法
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
//获取当前值
float animatedValue = (float)updatedAnimation.getAnimatedValue();
//改变textView位置
//textView.setTranslationX(animatedValue);
}
});
//开始动画
animation.start();
从上面代码可以发现并没有什么动画的现象,是的ValueAnimator的作用就是如此,总的来说,这段代码的作用的就:
在1000ms内变化0f到100f,并且可以在onAnimationUpdate回调中得到实时值。如果想要用ValueAnimator做View的动画,可以在回调中处理如上的textView.setTranslationX(animatedValue)
3.TypeEvaluator
这个接口代表变化的值类型(如上面的ofFloat(0f, 100f)中就指定了变化的值类型是float),来看看TypeEvaluator的源码
public interface TypeEvaluator<T> {
/**
* @param fraction 动画时间百分百,代表当前处于那个时间点(即是 已过时间/总时间)
* @param startValue 动画开始值,如上0f
* @param endValue 动画结束值,如上100f
* @return 返回动画当前值,所以不同的TypeEvaluator在evaluate中有不同的计算当前值的算法
*
*/
public T evaluate(float fraction, T startValue, T endValue);
}
TypeEvaluator是一个接口,必须继承他来实现自己的TypeEvaluator,下面是android定义好的TypeEvaluator的算法
//float类型和TypeEvaluator
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
//算法很简单,返回的当前值就是:(结束值 - 开始值)/ 百分比
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
//int类型的TypeEvaluator
public class IntEvaluator implements TypeEvaluator<Integer> {
//算法和float类型的一样
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
//颜色类型的TypeEvaluator
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
public static ArgbEvaluator getInstance() {
return sInstance;
}
//开启来比较负责,当时具体的算法不过管,只要明白这就是要改变颜色值是当前值的计算方法
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
}
ValueAnimator中不同的静态实例化方法就内部就是用了不同的TypeEvaluator
ValueAnimator ofInt(TypeEvaluator evaluator, Object… values)//利用IntEvaluator算法
ValueAnimator ofFloat(int… values)//利用FloatEvaluator算法
ValueAnimator ofArgb(int… values)//利用ArgbEvaluator值类型算法
ValueAnimator ofObject(TypeEvaluator evaluator, Object… values)//传入自定义的FloatEvaluator
其中的变化值是一个可变参数(比如传了0,100表示从0到100变化;传了0,50,100表示从0到50变化,再从50到100变化;传了30,10,60表示从30到10变化,再从10到60变化。只传一个值代表结束值)
4.Interpolator
插值器,和上一篇属性动画的插值器一样代表变化的快慢,如加速变化,减速变化等等。TimeInterpolator接口源码
public interface TimeInterpolator {
该接口定义类插值器规则,input的值在0和1之间代表时间百分百,返回值代表在当前时间的变化值的百分百(说白了就是这个时候变化到哪了),返回值可以是大于1或小于0(可以超越开始值和结束值,比如AnticipateInterpolator这个插值器,开始是会往回拉一下再往前走)。
float getInterpolation(float input);
}
实现这个接口并覆写getInterpolation就可以自定义Interpolator了,下面是android定义好的Interpolator
AccelerateDecelerateInterpolator
AccelerateInterpolator
AnticipateInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator
OvershootInterpolator
具体效果自己试下就知道了, ValueAnimator中setInterpolator(TimeInterpolator value)用于设置插值器。
5.ObjectAnimator
ObjectAnimator类是ValueAnimator的子类,他不像ValueAnimator要监听onAnimationUpdate来时view执行动画,它要传要执行动画的对象(不一定是view对象)即可,因为ObjectAnimator内部已经实现好了onAnimationUpdate。ObjectAnimator是属性最主要的类,因为在它上才体现属性的变化(ValueAnimator只是值的变化)。来看看怎么用吧。
//textView:要改变的属性的对象,指定对象的translationX属性名,后面是一个可变参数,和ValueAnimator类似
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
ObjectAnimator对属性的改变必须遵循下面的规则才有效
1.指定对象中必须要有指定属性的以驼峰命名的set方法(textView对象中必须要有setTranslationX方法)
2.如果后面的可变参数值传一个,则指定对象中必须要有指定属性的以驼峰命名的get方法(textView对象中必须要有getTranslationX方法)
3.以上两点的get的参数类型和set方法的返回值必须和ofXXX方法的类型相同(setTranslationX方法的参数类型,getTranslationX的返回必须和ofFloat类型相同,即float类型)
6. AnimatorSet
属性集合,可以同时或先后执行多个属性动画
AnimatorSet bouncer = new AnimatorSet();
//执行squashAnim1后执行bounceAnim
bouncer.play(bounceAnim).before(squashAnim1);
//同时执行squashAnim1后执行squashAnim2
bouncer.play(squashAnim1).with(squashAnim2);
//执行squashAnim1后执行bounceAnim
bouncer.play(bounceAnim).after(squashAnim1);
//调用strart方法上面添加的动画才会执行
animatorSet.start();
AnimatorSet的几个方法play,before,after,with的参数都是Animator对象,并将同时都返回原来的AnimatorSet。
7.Keyframe和PropertyValuesHolder
Keyframe包含了一个时间和值的键值对,代表某一时刻对应的值应该是多少,并将不用的Keyframe可以设置不同的Interpolator
//和ObjectAnimator一样Keyframe有三种类型:int float Object
//fraction为时间百分比,value为对应时刻的值
Keyframe.ofInt(float fraction, int value)
Keyframe.ofFloat(float fraction, floatvalue)
Keyframe.ofIObject(float fraction, Object value)
PropertyValuesHolder可以把多个Keyframe和指定属性集成起来,然后把集成好的PropertyValuesHolder传给指定对象使用。
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
//根据需要可以跟KeyFrame设置Interpolator
//kf0.setInterpolator(TimeInterpolator interpolator);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
//集成kf0,kf1,kf3,并传入rotation属性名
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
利用ofPropertyValuesHolder方法把PropertyValuesHolder和target对象关联起来生成ObjectAnimator对象
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000);
//执行动画
rotationAnim.start();
其实ObjectAnimator的ofXXX方法内部就是通过PropertyValuesHolder实现的,PropertyValuesHolder有对应的ofXXX方法。
8.属性动画在view控件上的使用
要让控件执行动画,有下面的几种方法
1.通过上的知识我们可以通过ValueAnimator设置监听来改变控件位置,但这样做比较麻烦。
2.利用ObjectAnimator出入指定view和属性,这个可以,那么view具体有那些属性可以执行动画的呢,有下面几个:
translationX , translationY: 平移x坐标y坐标,执行动画后对应控件的getLeft,getRight,getTop,getBottom返回值依然不会改变,但点击是热区是变了。
rotation, rotationX, rotationY:旋转,这个旋转属性是3d效果的
scaleX , scaleY: 缩放属性
pivotX ,pivotY:
x ,y:和translationX ,translationY效果一样只是,x属性值 = translationX 属性值 + getLeft返回值,y同理
alpha: 淡入淡出
看下例子
//直接使用ObjectAnimator 和AnimatorSet
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
//使用PropertyValuesHolder
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
//只是最简单的方法View的animate()可以返回一个ViewPropertyAnimator对象,通过他我可以直接调用
//上面列出的view的属性的对应方法来执行动画,ViewPropertyAnimator可以设置动画时间和Interpolator
myView.animate().x(50f).y(100f);
9.在res资源文件中定义属性动画
和上一篇的视图动画不同,属性动画要放到res/animator路径下,在xml下以下的标签对应不同的类对象
ValueAnimator - <animator>
ObjectAnimator -<objectAnimator>
AnimatorSet - <set>
<set
android:ordering=["together" | "sequentially" 代表同事执行还是顺序执行,默认为together]>
<objectAnimator
android:propertyName="属性名称"
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>
例子,在res/animator新建resproperty_animator.xml文件
<set android:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="intType"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>
</set>
在代码中获取该动画
AnimatorSet set =(AnimatorSet)AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
//一定要用setTarget方法把动画设置到相应对象中
set.setTarget(myObject);
set.start();