ValueAnimator是之前提到过的ObjectAnimator的父类;
它使用起来不像ObjectAnimator这么简单,但可控性更高,更能发挥想象力;
一般来说,使用ValueAnimator需要通过以下几步;
public void setObjectValues(Object... values) | 设置一组值,动画会根据这组值执行; 不同于ObjectAnimator,这里的参数不能只有一个值; 注意,这里参数是Object,这就给了很大的想象空间; |
void setEvaluator(TypeEvaluator value) | 设置求值程序; TypeEvaluator 是一个接口; public interface TypeEvaluator public T evaluate(float fraction, T startValue, T endValue); } 通过evaluate方法返回对应的值; 真个动画过程中evaluate方法会被调用很多次,直到动画播放结束; |
void addUpdateListener(AnimatorUpdateListener listener) | 主要是AnimatorUpdateListener; 它也是一个接口,返回动画过程中evaluate计算的记过; public static interface AnimatorUpdateListener { } |
1)既然是平移运动,就得先有一个坐标,故创建类MyPoint;
这是之后传入setObjectValues的参数,也是TypeEvaluator中evaluate用来计算的值,同时也是onAnimationUpdate获取的返回值;
public class MyPoint {
public float x;
public float y;
}
2)实现求值程序
直线运动的话,一个简单的方程就可以了;
public class MyEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
MyPoint pointS = (MyPoint)startValue;
MyPoint pointE = (MyPoint)endValue;
float x = pointS.x + (pointE.x - pointS.x) * fraction;
float y = pointS.y + (pointE.y - pointS.y)* fraction;
return new MyPoint(x, y);
}
}
3)使用
值通过ArrayList保存,用toArray方法传参;
在onAnimationUpdate中,通过ValueAnimator 的getAnimatedValue拿到计算后的返回值;
示例中的test是一个ImageView;
ArrayList values = new ArrayList<>();
values.add(new MyPoint(0, 0));
values.add(new MyPoint(100, 200));
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setObjectValues(values.toArray());
valueAnimator.setEvaluator(new MyEvaluator());
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MyPoint point = (MyPoint) animation.getAnimatedValue();
test.setTranslationX(point.x);
test.setTranslationY(point.y);
test.postInvalidate();
}
});
valueAnimator.setDuration(1200);
valueAnimator.start();
执行以上代码后,test视图会沿着直线运动,耗时1.2s;
当然,示例中可以传入更多的参数,让视图在运动中拐弯;
ArrayList values = new ArrayList<>();
values.add(new MyPoint(0, 0));
values.add(new MyPoint(100, 200));
values.add(new MyPoint(200, 100));
values.add(new MyPoint(50, 50));
通过上面的例子,可以看到,运动路径的关键点是MyEvaluator这个求值程序;
只要将它的求值方法改为对应的函数,就是让视图做各种函数运动了;
比如,抛物线,或者贝塞尔曲线之类的;
当然,抛物线的话,对输入参数的本身也是需要满足求值程序内的抛物线函数的;
例如:
public class MyEvaluator2 implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
MyPoint pointS = (MyPoint)startValue;
MyPoint pointE = (MyPoint)endValue;
float x = pointS.x + (pointE.x - pointS.x) * fraction;
/*
Y = X * X / 50;
*/
float y = (float) x * x / 50;
return new MyPoint(x, y);
}
}
使用的地方:
/*
Y = X * X / 50;
*/
ArrayList values = new ArrayList<>();
values.add(new MyPoint(0, 0));
values.add(new MyPoint(200, 800));
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setObjectValues(values.toArray());
valueAnimator.setEvaluator(new MyEvaluator2());
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MyPoint point = (MyPoint) animation.getAnimatedValue();
test.setTranslationX(point.x);
test.setTranslationY(point.y);
test.postInvalidate();
}
});
valueAnimator.setDuration(1200);
valueAnimator.start();
如果要用贝塞尔曲线的话,则需要修改MyPoint对象;
用三次贝塞尔举个例子:
public class MyPoint3 {
public float x;
public float y;
public float x1;
public float y1;
public float x2;
public float y2;
public MyPoint3(float x, float y, float x1, float y1, float x2, float y2) {
super();
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
}
贝塞尔曲线需要辅助坐标,示例中以结束位置x1,y1,x2,y2作为辅助坐标;
求值程序:
public class MyEvaluator3 implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
float one_fraction = 1 - fraction;
MyPoint3 pointS = (MyPoint3)startValue;
MyPoint3 pointE = (MyPoint3)endValue;
float x = pointS.x * one_fraction * one_fraction *one_fraction
+ 3 * pointE.x1 * fraction * one_fraction * one_fraction
+ 3 * pointE.x2 * fraction * fraction * one_fraction
+ pointE.x * fraction * fraction *fraction;
float y = pointS.y * one_fraction * one_fraction *one_fraction
+ 3 * pointE.y1 * fraction * one_fraction * one_fraction
+ 3 * pointE.y2 * fraction * fraction * one_fraction
+ pointE.y * fraction * fraction *fraction;
return new MyPoint3(x, y, 0, 0, 0, 0);
}
}
使用:
ArrayList values = new ArrayList<>();
values.add(new MyPoint3(0, 0, 0, 0, 0, 0));
values.add(new MyPoint3(250, 0, 400, 650, 600, 100));
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setObjectValues(values.toArray());
valueAnimator.setEvaluator(new MyEvaluator3());
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MyPoint3 point = (MyPoint3) animation.getAnimatedValue();
test.setTranslationX(point.x);
test.setTranslationY(point.y);
test.postInvalidate();
}
});
valueAnimator.setDuration(1200);
valueAnimator.start();
多次贝塞尔曲线可以完成比较柔和的曲线运动;
具体方程式百度一下就能得到;
给MyPoint加上一个枚举,表示运动类型;
在求值程序中根据终点的运动类型选择不同函数;
public class MyPoint4 {
public enum Type {
line,
bezier3
};
public Type type;
public float x;
public float y;
public float x1;
public float y1;
public float x2;
public float y2;
public MyPoint4(float x, float y, float x1, float y1, float x2, float y2) {
super();
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
type = Type.bezier3;
}
public MyPoint4(float x, float y) {
super();
this.x = x;
this.y = y;
type = Type.line;
}
}
求值程序:
public class MyEvaluator4 implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
float one_fraction = 1 - fraction;
MyPoint4 pointS = (MyPoint4)startValue;
MyPoint4 pointE = (MyPoint4)endValue;
float x = 0;
float y = 0;
switch (pointE.type) {
case line: {
x = pointS.x + (pointE.x - pointS.x) * fraction;
y = pointS.y + (pointE.y - pointS.y)* fraction;
break;
}
case bezier3: {
x = pointS.x * one_fraction * one_fraction *one_fraction
+ 3 * pointE.x1 * fraction * one_fraction * one_fraction
+ 3 * pointE.x2 * fraction * fraction * one_fraction
+ pointE.x * fraction * fraction *fraction;
y = pointS.y * one_fraction * one_fraction *one_fraction
+ 3 * pointE.y1 * fraction * one_fraction * one_fraction
+ 3 * pointE.y2 * fraction * fraction * one_fraction
+ pointE.y * fraction * fraction *fraction;
break;
}
}
return new MyPoint4(x, y);
}
}
调用:
ArrayList values = new ArrayList<>();
values.add(new MyPoint4(0, 0));
values.add(new MyPoint4(100, 800));
values.add(new MyPoint4(800, 100));
values.add(new MyPoint4(0, 0));
values.add(new MyPoint4(250, 0, 400, 650, 600, 100));
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setObjectValues(values.toArray());
valueAnimator.setEvaluator(new MyEvaluator4());
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MyPoint4 point = (MyPoint4) animation.getAnimatedValue();
test.setTranslationX(point.x);
test.setTranslationY(point.y);
test.postInvalidate();
}
});
valueAnimator.setDuration(1200);
valueAnimator.start();
除了移动以外,ValueAnimator 还能做很多很多的事情,比如自定义View的动画之类的;
想象力够丰富,就能玩出新高度!