Android属性动画(三)——TypeEvaluator(估值器)和Interpolator(插值器)

前几篇文章我们介绍了ObjectAnimator和ValueAnimator的基本用法。

TypeEvaluator(估值器)

细心的朋友会发现我们有一个东西没有用到。

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

当我们ValueAnimator.ofObject()函数来做动画效果的时候就会用到估值器了,估值器说白了就是用来确定在动画过程中每时每刻动画的具体值得换句话说就是确定ValueAnimator.getAnimatedValue()返回的具体对象类型。

系统内置的估值器有:

  • IntEvaluator Int类型估值器,返回int类型的属性改变
  • FloatEvaluator Float类型估值器,返回Float类型属性改变
  • ArgbEvaluator 颜色类型估值器

TypeEvaluator源码:

package android.animation;

public interface TypeEvaluator<T> {

    public T evaluate(float fraction, T startValue, T endValue);

}
  • 第一个参数表示动画完成度。
  • 第二个和第三个参数分别是初始值和结束值。

ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,它内置了一个FloatEvaluator。

package android.animation;

/**
 * This evaluator can be used to perform type interpolation between <code>float</code> values.
 */
public class FloatEvaluator implements TypeEvaluator<Number> {

    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

ok,我们自己实现一个估值器实现一个动画效果。先看看效果。
TypeEvaluator

Point对象存放CircleView的属性

public class Point {
    private float mRadius;
    public float x;
    public float y;

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

    public Point() {
    }

    public float getRadius() {
        return mRadius;
    }

    public void setRadius(float mRadius) {
        this.mRadius = mRadius;
    }

    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;
    }
}

估值器,这里用了三角函数来计算x,y坐标

public class CircleTypeEvaluator implements TypeEvaluator {
    private float radius;//旋转的半径
    public CircleTypeEvaluator(float radius) {
        this.radius = radius;
    }
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point point = new Point();
        Point startPoint = (Point) startValue;
        //旋转的角度
        float angle = fraction * 360;
        float longY = (float) ((radius *(1- Math.cos(Math.toRadians(angle)))));//计算y坐标
        point.setY(startPoint.getY() + longY);
        float longX = (float) (radius - radius * Math.sin(Math.toRadians(angle)));//计算x坐标
        point.setX(startPoint.getX() + longX-radius);
        return point;
    }
}

CircleView

public class CircleView extends View {

    private Paint paint;
    private Point point;

    public CircleView(Context context) {
        super(context);
        init();
    }

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }


    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        paint=new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(getResources().getColor(android.R.color.holo_blue_light));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (point==null){
            point = new Point(getWidth()/2,getHeight()/2,100);
            canvas.drawCircle(point.x,point.y,point.getRadius(),paint);

        }else{
            canvas.drawCircle(point.getX(),point.getY(),point.getRadius(),paint);
        }
    }

    public void setViewPoint(Point result){
        point.setX(result.getX());
        point.setY(result.getY());
        invalidate();
    }

}

ok,使用的时候,这里球的半径是固定的100

circleView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Point start = new Point(circleView.getWidth()/2,circleView.getHeight()/2,100);

        ValueAnimator animator = ValueAnimator.ofObject(new CircleTypeEvaluator(200),start,start);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point result = (Point) animation.getAnimatedValue();
                circleView.setViewPoint(result);
            }
        });
        animator.setDuration(2000);
        animator.setRepeatCount(-1);
        animator.start();
     }
 });

Interpolator(插值器)

在动画的播放过程中Android中提供插值器来改变动画的播放速率,采用不用的插值器来实现不同的播放效果。

大家有没有发现上面我们讲TypeEvaluator的例子,蓝色的小球运动的速率是由慢到快再变慢,因为系统默认的使用的插值器是AccelerateDecelerateInterpolator默认实现这种效果。

Interpolator接口从Android 1.0版本开始就一直存在,我们讲Tween动画的时候就是用过。属性动画中新增了一个TimeInterpolator接口,Interpolator是继承TimeInterpolator接口的。系统已经有很多可以直接使用的类。

java类xml id值描述
AccelerateDecelerateInterpolator@android:anim/accelerate_decelerate_interpolator动画始末速率较慢,中间加速
AccelerateInterpolator@android:anim/accelerate_interpolator动画开始速率较慢,之后慢慢加速
AnticipateInterpolator@android:anim/anticipate_interpolator开始的时候从后向前甩
AnticipateOvershootInterpolator@android:anim/anticipate_overshoot_interpolator类似上面AnticipateInterpolator
BounceInterpolator@android:anim/bounce_interpolator动画结束时弹起
CycleInterpolator@android:anim/cycle_interpolator循环播放速率改变为正弦曲线
DecelerateInterpolator@android:anim/decelerate_interpolator动画开始快然后慢
LinearInterpolator@android:anim/linear_interpolator动画匀速改变
OvershootInterpolator@android:anim/overshoot_interpolator向前弹出一定值之后回到原来位置
PathInterpolator新增,定义路径坐标后按照路径坐标来跑

使用方法很简单

animator.setInterpolator(new AccelerateInterpolator());

自定义Interpolator

有时候系统提供的实现类并不能满足我们的需求,所以就需要自定义Interpolator。
所有的插值器都要去实现TimeInterpolator接口,TimeInterpolator接口代码如下。

public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

从代码中可以看到这里只需要实现getInterpolation()函数就好了。

TimeInterpolator接口里面getInterpolation函数的参数input就是动画每一次播放过程中的时间比例值,是0-1之间的值,但是返回的值是可以大于1,也是可以小于0的,对于返回值倒是没什么特定的要求。

我们先看看系统的插值器是怎么实现的,AccelerateDecelerateInterpolator :

/**
 * An interpolator where the rate of change starts and ends slowly but
 * accelerates through the middle.
 */
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}
  • 构造函数
    第一个是无参构造是我们具体JAVA代码中new的时候用到,第二个构造函数是在资源文件里面使用的时候调用的(@android:anim/accelerate_decelerate_interpolator的时候会调用这个构造方法)。
  • getInterpolation()方法
    AccelerateDecelerateInterpolator插值器的曲线图得到对应的数学表达式(cos((x + 1) * PI) / 2.0) + 0.5,然后数学表达式转换为代码形式,(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f。

我们开始定义自己的Interpolator。
第一步确定我们要实现的插值器的曲线图。
第二步数学表达式,需要一定的数学功底
pow(2, -10 * x) * sin((x - factor / 4) * (2 * PI) / factor) + 1
factor = 0.2 这个我们在构造函数的时候指定
第三步

public class SpringInterpolator implements Interpolator {

    private static final float DEFAULT_FACTOR = 0.2f;

    private float mFactor;

    public SpringInterpolator() {
        this(DEFAULT_FACTOR);
    }

    public SpringInterpolator(float mFactor) {
        this.mFactor = mFactor;
    }

    @Override
    public float getInterpolation(float input) {
        // pow(2, -10 * input) * sin((input - factor / 4) * (2 * PI) / factor) + 1
        return (float) (Math.pow(2, -10 * input) * Math.sin((input - mFactor / 4.0d) * (2.0d * Math.PI) / mFactor) + 1);
    }
}
animator.setInterpolator(new SpringInterpolator());

看看效果
SpringInterpolator

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值