之前讲了ValueAnimator,但是有个缺点,就是只能对数值对动画进行计算,我们要想对哪个控件操作,需要监听动画过程,在监听中对控件操作。这样使用起来相比补间动画而言就相对比较麻烦。
为了能让动画直接与对应控件相关联,以使我们从监听动画过程中解放出来,谷歌的开发人员在ValueAnimator的基础上,又派生了一个类ObjectAnimator。
由于ObjectAnimator是派生自ValueAnimator的,所以ValueAnimator中所能使用的方法,在ObjectAnimator中都可以正常使用。
但是ObjectAnimator也重写了几个方法,比如ofInt(),ofFloat(),我们先看看用ObjectAnimator的ofFloat如何实现一个动画。
1.使用
public class MainActivity extends ActionBarActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textview);
}
public void start(View view){
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "alpha", 1,0,1);
animator.setDuration(2000);
animator.start();
}
}
效果如下
我们这里使用的构造方法为
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
第一个参数为添加动画的对象
第二个参数为动画属性名称,这里我们使用alpha透明度动画
第三个参数为要改变的值,是可变的,这里我们从1变为0再变为1,也就是从不透明变成透明,然后变回来。
下面我们在看看如何实现旋转效果
public void start(View view){
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "rotation", 0,180,0);
animator.setDuration(2000);
animator.start();
}
效果如下:
从代码中可以看到,我们只需要改变ofFloat()的第二个参数的值就可以实现对应的动画。
那么问题来了,我们怎么知道第二个参数的值是啥呢?
2.set函数
我们在会看构造ObjectAnimator时的构造方法
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "rotation", 0,180,0);
TextView有rotation这个属性吗,没有,连View中也没有,那它是怎么改变这个值的?其实,ObjectAnimator做动画,并不是根据控件xml中的属性来改变的,而是通过指定属性所对应的set方法来改变的。比如,我们上面指定的改变rotation的属性值,ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setRotation()方法来改变控件中对应的值。同样的道理,当我们在最开始的示例代码中,指定改变”alpha”属性值的时候,ObjectAnimator也会到TextView中去找对应的setAlpha()方法。那TextView中都有这些方法吗,有的,这些方法都是从View中继承过来的,在View中有关动画,总共有下面几组set方法:
//1、透明度:alpha
public void setAlpha(float alpha)
//2、旋转度数:rotation、rotationX、rotationY
public void setRotation(float rotation)
public void setRotationX(float rotationX)
public void setRotationY(float rotationY)
//3、平移:translationX、translationY
public void setTranslationX(float translationX)
public void setTranslationY(float translationY)
//缩放:scaleX、scaleY
public void setScaleX(float scaleX)
public void setScaleY(float scaleY)
可以看到在View中已经实现了有关alpha,rotaion,translate,scale相关的set方法。所以我们在构造ObjectAnimator时可以直接使用。
1、要使用ObjectAnimator来构造对画,要操作的控件中,必须存在对应的属性的set方法
2、setter 方法的命名必须以骆驼拼写法命名,即set后每个单词首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName
下面我们就来看一下上面中各个方法的使用方法及作用。
(1)setRotation,setRotationX,setRotationY
setRotation:绕z轴转
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "rotation", 0,180,360);
animator.setDuration(2000);
animator.start();
setRotationX:绕X轴转
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "rotationX", 0,180,360);
animator.setDuration(2000);
animator.start();
setRotationY:绕Y轴转
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "rotationY", 0,180,360);
animator.setDuration(2000);
animator.start();
(2) setTranslationX,setTranslationY
setTranslationX(float translationX):X方向移动的距离,以当前控件为原点,向右为正方向,参数translationX表示移动的距离。
setTranslationY(float translationY):
Y方向移动的距离,以当前控件为原点,向右为正方向,参数translationY表示移动的距离。
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "translationX", 0,200,-200,0);
animator.setDuration(2000);
animator.start();
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "translationY", 0,200,-200,0);
animator.setDuration(2000);
animator.start();
(3)setScaleX(float scaleX) :在X轴上缩放,scaleX表示缩放倍数
setScaleY(float scaleY) ::在Y轴上缩放,scaleY表示缩放倍数
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "scaleX", 0,3,2,0);
animator.setDuration(2000);
animator.start();
我们可以看到,TextView在X方向从0倍变为3倍,然后变为2倍,最后又变为0倍,也就是消失了。
ObjectAnimator animator = ObjectAnimator.ofFloat(textView, "scaleY", 0,3,2,0);
animator.setDuration(2000);
animator.start();
可以看到ObjectAnimator的动画流程中,也是首先通过加速器产生当前进度的百分比,然后再经过Evaluator生成对应百分比所对应的数字值。这两步与ValueAnimator是完全一样的,唯一不同的是最后一步,在ValueAnimator中,我们要通过添加监听器来监听当前数字值。而在ObjectAnimator中,则是先根据属性值拼装成对应的set函数的名字,比如这里的scaleY的拼装方法就是将属性的第一个字母强制大写后,与set拼接,所以就是setScaleY。然后通过反射找到对应控件的setScaleY(float scaleY)函数,将当前数字值做为setScaleY(float scale)的参数将其传入。
这里在找到控件的set函数以后,是通过反射来调用这个函数的,
好了,在知道了ObjectAnimator的原理以后,下面就来看看如何自定义一个ObjectAnimator的属性吧。
我们自定义一个圆形,然后将它的半径从0变为3倍然后变为1倍,下面看看怎么实现
1.保存圆信息的Point类
public class Point {
private float radius;
public Point(float radius) {
this.radius = radius;
}
public void setRadius(float radius) {
this.radius = radius;
}
public float getRadius() {
return radius;
}
}
只有一个属性,就是半径
2.自定义的View,画一个圆形
public class MyPointView extends View{
//创建圆形对象,半径为100
private Point point = new Point(100);
public MyPointView(Context context) {
super(context);
}
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setStyle(Style.FILL);
//以300,300为圆心,以当前圆的半径为半径,画一个圆
canvas.drawCircle(300,300, point.getRadius(), paint);
super.onDraw(canvas);
}
public void setPointRadius(float radius){
point.setRadius(radius);
invalidate();
}
}
我们来看看这个setPointRadius方法:
第一点:这个方法对应的属性是pointRadius或者PointRadius,第一个字母大小写不是强制的,后面的字母必须与set函数一致
第二点:在setPointRadius中,先将当前动画传过来的值保存到mPoint中,做为当前圆形的半径。然后强制界面刷新
3.java代码,
public class MainActivity extends ActionBarActivity {
private MyPointView pointView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pointView = (MyPointView) findViewById(R.id.pointview);
}
public void start(View view){
ObjectAnimator animator = ObjectAnimator.ofFloat(pointView, "pointRadius", 0,300,100);
animator.setDuration(2000);
animator.start();
}
}
我们可以看到,我们使用了pointRadius这个属性,对应的就是MyPointView类中的方法setPointRadius,点击按钮后,启动动画,ObjectAnimator就实时的把参数的值传递给setPointRadius这个函数,由于我们在这个函数中将半径设置给了圆形,然后重新绘制界面,所以可以看到圆的半径在不断变化。
3.何时需要实现对应属性的get函数
我们之前看到的ObjectAnimator的构造函数的第三个参数都是可变的,
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
那么我们如果传入一个参数会怎么样
ObjectAnimator animator = ObjectAnimator.ofFloat(pointView, "pointRadius", 100);
animator.setDuration(2000);
animator.start();
我们可以看到,半径从0变为100,说明是从0开始变得,但是此时控制台已经给出了警告
为什么呢,因为当我们只设置一个值的时候,系统会调用get对应的属性值,如果找不到,就会使用默认的值开始动画,比如int类型为0,此时就会报出一个警告,如果给控件设置了get函数,就会从get函数对应的值开始动画。
public float getPointRadius(){
return 50;
}
我们可以看到,圆的半径是从50开始的,而不是0.
最后我们总结一下:当且仅当动画的只有一个过渡值时,系统才会调用对应属性的get函数来得到动画的初始值。
最后我们看看如何使用使用ArgbEvaluator
我们知道TextView有个属性可以改变背景颜色
public void setBackgroundColor(int color)
大家可以回想到,我们在ValueAnimator中也曾改变过背景色,使用的是ArgbEvaluator。在这里我们再回顾下ArgbEvaluator,它的实现代码如下:
public class ArgbEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24);
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24);
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))));
}
}
这里主要说一下,ArgbEvaluator 返回值类型是int,所以我们要使用ofInt来实现动画
ObjectAnimator animator = ObjectAnimator.ofInt(textview, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
animator.setDuration(2000);
animator.setEvaluator(new ArgbEvaluator());
animator.start();