ValueAnimator

ValueAnimator是之前提到过的ObjectAnimator的父类;

它使用起来不像ObjectAnimator这么简单,但可控性更高,更能发挥想象力

一般来说,使用ValueAnimator需要通过以下几步;

  • setObjectValues
  • setEvaluator
  • addUpdateListener
  • setDuration
  • start
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 {
    void onAnimationUpdate(ValueAnimator animation);

}

以下用平移动画举一些例子

一、 简单的直线运动

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的动画之类的;

想象力够丰富,就能玩出新高度!

你可能感兴趣的:(动画,Android)