Android动画深入分析-属性动画中级用法:TypeEvaluator

要点

1、TypeEvaluator和ofObject
2、自定义ValueAnimator的TypeEvaluator使用
3、自定义ObjectAnimator的TypeEvaluator使用

一、TypeEvaluator和ofObject

1、TypeEvaluator

那么TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。我们在上一篇文章中学到的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);
    }
}
//用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。

get:
1、FloatEvaluator 实现了TypeEvaluator 接口
2、FloatEvaluator 重写了evaluate方法
3、evaluate方法参数:

  • float fraction 表示动画的完成度,我们应该根据它来计算当前动画的值应该是多少。
  • Object startValue、Object endValue 动画的初始值和结束值。

4、代码意思:用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。

2、ofObject

前面我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。

二、ValueAnimator的TypeEvaluator

补间动画只能对view进行操作,属性动画可以对任意对象进行操作。假如我们有个自定义view,这个view中有个Point对象用于坐标管理,然后在onDraw方法中根据point的坐标进行绘制。当我们对view的point进行动画操作。那么自定义view动画就出来了。以下便实现下这个栗子。。。。

1、Point类的定义
/**
 * Created by sunnyDay on 2019/6/11 15:01
 */
public class Point {
    private float x;
    private float y;

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }
}

2、PointEvaluator定义
import android.animation.TypeEvaluator;
/**
 * Created by sunnyDay on 2019/6/11 15:05
 */
public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        // 计算变化的x,y值。 得出变化的点。
        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、如何对我们自定义的Point对象进行动画操作

假如我们有点point1(0,0)、point2(300,300)现在讲point1通过平滑过渡到point2(如下代码)

        Point point1 = new Point(0,0);
        Point point2 = new Point(360,360);
        
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),point1,point2);
        valueAnimator.setDuration(3000);
        valueAnimator.start();

ofObject 方法源码:

     * @param evaluator A TypeEvaluator that will be called on each animation frame to
     * provide the ncessry interpolation between the Object values to derive the animated
     * value.
     * @param values A set of values that the animation will animate between over time.
     * @return A ValueAnimator object that is set up to animate between the given values.
     */
    public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setObjectValues(values);
        anim.setEvaluator(evaluator);
        return anim;
    }

1、TypeEvaluator evaluator TypeEvaluator 类型的对象
2、Object… values 可变参数对象(对象类型参数)

4、MyCustomView的代码实现
/**
 * Created by sunnyDay on 2019/6/11 15:23
 */
public class MyCustomView extends View {
    public static final float RADIUS = 50f;
    private Point currentPoint;
    private Paint mPaint;


    public MyCustomView(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); // 画圆
            pointStartAnimation(); // 开启动画
        } else {
            drawCircle(canvas);
        }

    }

    /**
     * 给自定义的Point对象加动画
     */
    private void pointStartAnimation() {
        Point startPoint = new Point(RADIUS, RADIUS); // A(50,50) 点
        Point endPoint = new Point(300, 300);  // B(300,300) 点

        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);// 从A点到B点
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 更新时不断赋值  通知重绘
                currentPoint = (Point) animation.getAnimatedValue();
                invalidate(); // 重走 onDraw
            }
        });
        valueAnimator.setDuration(5000);
        valueAnimator.start();
    }

    /**
     * 用画笔在画布画圆
     */
    private void drawCircle(Canvas canvas) {
        float x = currentPoint.getX();
        float y = currentPoint.getY();
        canvas.drawCircle(x, y, RADIUS, mPaint);
    }
}

在xml中就可简单的使用这个控件了

三、ObjectAnimator的TypeEvaluator

1、我们知道补间动画只能进行那四种动画,或者那四种的组合。扩展性太差了,如果我们想要控件的颜色进行动态的改变,补间动画就做不到了。
2、大家应该都还记得,ObjectAnimator内部的工作机制是通过寻找特定属性的get和set方法,然后通过方法不断地对值进行改变,从而实现动画效果的。因此我们就需要在MyCustomView中定义一个color属性,并提供它的get和set方法。这里我们可以将color属性设置为字符串类型,使用#RRGGBB这种格式来表示颜色值。(结合上面栗子整合如下)

1、color属性的添加
private String color;
  public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
        // 改变颜色时 通知重绘
        mPaint.setColor(Color.parseColor(color));
        invalidate();
    }
2、TypeEvaluator的编写
/**
 * Created by sunnyday on 2019/6/11 16:43
 */
public class ColorEvaluator implements TypeEvaluator {
    private int mCurrentRed = -1;
    private int mCurrentGreen = -1;
    private int mCurrentBlue = -1;

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        String startColor = (String) startValue;
        String endColor = (String) endValue;
        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
        // 初始化颜色的值
        if (mCurrentRed == -1) {
            mCurrentRed = startRed;
        }
        if (mCurrentGreen == -1) {
            mCurrentGreen = startGreen;
        }
        if (mCurrentBlue == -1) {
            mCurrentBlue = startBlue;
        }
        // 计算初始颜色和结束颜色之间的差值
        int redDiff = Math.abs(startRed - endRed);
        int greenDiff = Math.abs(startGreen - endGreen);
        int blueDiff = Math.abs(startBlue - endBlue);
        int colorDiff = redDiff + greenDiff + blueDiff;
        if (mCurrentRed != endRed) {
            mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
                    fraction);
        } else if (mCurrentGreen != endGreen) {
            mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
                    redDiff, fraction);
        } else if (mCurrentBlue != endBlue) {
            mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
                    redDiff + greenDiff, fraction);
        }
        // 将计算出的当前颜色的值组装返回
        String currentColor = "#" + getHexString(mCurrentRed)
                + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
        return currentColor;
    }

    /**
     * 根据fraction值来计算当前的颜色。
     */
    private int getCurrentColor(int startColor, int endColor, int colorDiff,
                                int offset, float fraction) {
        int currentColor;
        if (startColor > endColor) {
            currentColor = (int) (startColor - (fraction * colorDiff - offset));
            if (currentColor < endColor) {
                currentColor = endColor;
            }
        } else {
            currentColor = (int) (startColor + (fraction * colorDiff - offset));
            if (currentColor > endColor) {
                currentColor = endColor;
            }
        }
        return currentColor;
    }

    /**
     * 将10进制颜色值转换成16进制。
     */
    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }

}

3、添加到动画中
 /**
     * 给自定义的Point对象加动画
     */
    private void pointStartAnimation() {
        Point startPoint = new Point(RADIUS, RADIUS); // A(50,50) 点
        Point endPoint = new Point(300, 300);  // B(300,300) 点

        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);// 从A点到B点
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 更新时不断赋值  通知重绘
                currentPoint = (Point) animation.getAnimatedValue();
                invalidate(); // 重走 onDraw
            }
        });
        ObjectAnimator anim2 = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(),
                "#0000FF", "#FF0000");

        // 动画集合
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(valueAnimator).with(anim2);
        animSet.setDuration(5000);
        animSet.start();

    }

end

参考与:https://blog.csdn.net/guolin_blog/article/details/43816093

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值