在上一篇文章已经讲过属性动画的工作步骤如下:
Property Animation动画有两个步聚:
1.计算属性值
2.为目标对象的属性设置属性值,即应用和刷新动画
1.计算属性值
以下分析引入了两个概念:已完成动画分数(elapsed fraction)、插值分数( interpolated fraction )。
(1)过程一:计算已完成动画分数 elapsed fraction
为了执行一个动画,你需要创建一个ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用start后的整个动画过程中, ValueAnimator会根据已经完成的动画时间计算得到一个0到1之间的分数,代表该动画的已完成动画百分比。0表示0%,1表示100%。
(2)过程二:计算插值(动画变化率)interpolated fraction
当ValueAnimator计算完已完成动画分数后,它会调用当前设置的TimeInterpolator,去计算得到一个interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。
(3)过程三:计算属性值
当插值分数计算完成后,ValueAnimator 会根据插值分数调用合适的 TypeEvaluator去计算运动中的属性值。
TimeInterpolator和TypeEvaluator的区别
首先明确动画属性值的计算包括三步,其中第二步和第三步分别需要借助TimeInterpolator和TypeEvluator完成。TypeEvaluator所做的是根据数据结构计算最终的属性值,允许你定义自己的数据结构,这是官方对它的真正定义,如果你所定义的属性值的数据类型不是float、int、color类型,那么你需要实现TypeEvaluator接口的evaluate()方法,自己进行属性值的计算Interpolator更倾向于你定义一种运动的变化率,比如匀速、加速、减速等。
1.1Interpolators类
就是插值器,详细的情况之前一片文章介绍了,Tween动画的插值器同样适用于属性动画。在这里不细讲。设置就是 anim.setInterpolator(new AccelerateInterpolator(2f));
1.2 TypeEvaluator类
这个类的作用简单来说,就是告诉动画系统如何从初始值过渡到结束值。它们通过Animator提供的动画的起始和结束值去计算一个动画的属性值。属性系统提供了以下几种Evaluators:
- IntEvaluator
- FloatEvaluator
- ArgbEvaluator
这三个由系统提供,分别用于计算int,float,color型(十六进制)属性的计算器- TypeEvaluator
一个用于用户自定义计算器的接口,如果你的对象属性值类型,不是int,float,或者color类型,你必须实现这个接口,去定义自己的数据类型。TypeEvaluator接口只有一个方法,就是evaluate()方法,它允许你使用的animator返回一个当前动画点的属性值。
ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
代码分析:
很明显FloatEvaluator 是实现了TypeEvaluator 接口,重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。知道这些参数含义,那以上代码就容易看懂的多。
1.2.1那如何实现自定义TypeEvaluator
eg.需求:实现屏幕上的一个点到另一个点的平滑过渡
1.定义自定义POJO类
public class Point {
private float x;
private float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
就是用来屏幕上的某一点有x,y属性
2.定义自定义TypeEvaluator
public class PointEvaluator implements TypeEvaluator{
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
Point point = new Point(x, y);
return point;
}
}
也可以这么写
public class PointEvaluator implements TypeEvaluator<Point>{
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
Point startPoint = startValue;
Point endPoint = endValue;
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
Point point = new Point(x, y);
return point;
}
}
3.使用自定义TypeEvaluator
3.1简单使用:
Point point1 = new Point(0, 0);
Point point2 = new Point(300, 300);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);
anim.setDuration(5000);
anim.start();
3.2拓展使用
实现一个圆形图从视图左上侧对角平滑到视图右下侧的自定义视图
public class MyAnimView extends View {
public static final float RADIUS = 50f;
private Point currentPoint;
private Paint mPaint;
public MyAnimView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
drawCircle(canvas);
startAnimation();
} else {
drawCircle(canvas);
}
}
private void drawCircle(Canvas canvas) {
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
private void startAnimation() {
Point startPoint = new Point(RADIUS, RADIUS);
Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setDuration(5000);
anim.start();
}
}