Android attribute animation (II) - and interpolator estimator

Disclaimer: This article is a blogger original article, please indicate the source: https://blog.csdn.net/qq_38182125/article/details/89631460

I. Overview

This article continued access Android animation properties (a) to introduce a very important attribute animation of two parts: an interpolation filter (Interpolator) and estimators (TypeEvaluator). And the variation of the animation are used to define them in the properties over time animation transition from an initial value to the end value calculation rule, structured as follows:

  • Interpolator (the Interpolator) introduction and use
  • Estimator (TypeEvaluator) introduction and use

Second, the interpolator Interpolator

Interpolator (the Interpolator) used to define the variation over time of the animation. Remark up more abstract, but in fact when we actually use property of animation, we can clearly feel the effect of the interpolation filter.

E.g. default deceleration interpolator employed, it will gradually begin to accelerate in the animation, and then gradually decelerated until the end of the animation; while pressing the linear interpolator is a uniform speed of performing a complete animation. They performed with variation of the ratio of the animation as follows:
Here Insert Picture Description Here Insert Picture Description
the slope of a point where the line drawing is the rate at a certain time. From FIG deceleration may be apparent to interpolator is gradually accelerated, and then gradually decelerated, while linear interpolation is performed at a rate remains constant completed. Knowing the role interpolator after, then we will look at what Android has built-in interpolator.

1. System built interpolator

CCP Android system built nine interpolator, as shown below:

Interpolation name effect
AccelerateDecelerateInterpolator Deceleration interpolator, the animation first acceleration, deceleration after
LinearInterpolator Linear interpolation, a uniform run animation
AccelerateInterpolator Acceleration interpolator, the animation speed up to the end of the run
DecelerateInterpolator Deceleration interpolator, the animation deceleration to the end
OvershootInterpolator 快速完成动画,超出终点一小部分后再回到终点
AnticipateInterpolator 先后退一小步再加速前进至结束
AnticipateOvershootInterpolator 先后退一小步再加速前进,超出终点一小部分后再回到终点
BounceInterpolator 弹性插值器,在动画结束之前会有一个弹性动画的效果
CycleInterpolator 周期运动

文字说明比较抽象,笔者下面通过图像的形式来展示这些插值器随时间流逝的变化效果:

Here Insert Picture Description Here Insert Picture Description Here Insert Picture Description Here Insert Picture Description Here Insert Picture Description Here Insert Picture Description Here Insert Picture Description
接下来我们就来看到插值器的相关接口以及如何自定义一个插值器。

2. TimeInterpolator接口

自定义插值器我们需要实现 Interpolator / TimeInterpolator 接口并实现接口方法getInterpolation。属性动画实现的是TimeInterpolator接口,而补间动画实现的则是Interceptor接口。事实上Interpolator接口继承了TimeInterceptor接口,这样做是基于兼容性的考虑,使得过去所有应用于补间动画的插值器都可以直接应用在属性动画上。为了我们的自定义插值器具有更好的兼容性,推荐实现Interpolator接口。

接下来看到TimeInterpolator接口的定义:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
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方法需要实现,它是用于定义动画随时间流逝的变化规律的,它的参数input表示的是当前动画执行比例,如0.5表示动画现在执行了50%。返回值表示动画的完成度,在属性动画中称之为fraction

我们通过系统内置的几个插值器类来看看如何使用这个方法,先看到LinearInterpolator

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

   ......
}

可以看到LinearInterpolator继承自BaseInterpolator,这是一个抽象类,它实现了Interpolator接口。所以LinearInterpolator相当于间接实现了Interpolator接口,接下来看到getInterpolation的实现:

public float getInterpolation(float input) {
    return input;
}

可以看到,它就是简单地将输入参数input返回了。虽然简单的不可思议,但是也很容易理解,结合前面的图,LinearInterpolator是随着时间流逝匀速变化的,所以它的变化是线性的,我们只需要直接返回input即可。接下来看一个稍微复杂些的AccelerateInterpolator,也就是加速度插值器:

public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    ......

    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }

    ......
}

在默认情况下,mFactor的值为1.0f,也就是说它的返回值是input*input,结合一定的数学知识和上面的相关图像我们很容易得知,在0~1这个区间上斜率是逐渐增加的,符合加速度插值器的特点。在知道了插值器是如何实现了之后,接下来我们来尝试自定义一个插值器。

3. 自定义插值器

接下来我们自定义一个减速加速插值器,称为DecelerateAccelerateInterpolator,它的变化图像如下所示:
Here Insert Picture Description
它的实现代码如下,由于主要是数学知识,这里就不再对代码进行赘述:

public class DecelerateAccelerateInterpolator implements Interpolator {

    @Override
    public float getInterpolation(float input) {
        input -= 0.5;
        return (float) Math.pow(input, 3) * 4.0f + 0.5f;
    }
}

应用到实际的动画中,代码如下:

ObjectAnimator anim = ObjectAnimator.ofFloat(mButton, "rotation", 0.0f, 360.0f);
anim.setDuration(5000);
anim.setInterpolator(new DecelerateAccelerateInterpolator());
anim.start();

效果如下所示:
Here Insert Picture Description
可以明显看到先减速后加速的效果,以上就是关于自定义插值器的简单示例了。接下来我们来介绍属性动画中另一个非常重要的部分:估值器。


三、估值器(TypeEvaluator)

估值器(TypeEvaluator)的作用是定义从初始值过渡到结束值的计算规则。当我们采用如下的方式创建属性动画时:

ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 360.0f);

ValueAnimator.ofFloat方法其实就实现了初始值到结束值过渡的规则定义,我们在使用过程中没有感受到估值器 TypeEvaluator 的存在是因为这个方法内置了FloatEvaluator来对浮点值从初始值到结束值进行了定义,在看FloatEvaluator的源码之前,我们先来查看每个估值器都要实现的接口:TypeEvaluator。

1. TypeEvaluator接口

TypeEvaluator接口的定义如下所示:

public interface TypeEvaluator<T> {
    public T evaluate(float fraction, T startValue, T endValue);
}

可以看到这个接口接收一个泛型参数T,用于其唯一的方法evaluate的参数类型以及返回值类型。该方法的参数含义以及返回值的含义如下表所示:

名称 含义
fraction 表示动画的完成度,它在属性动画中的取值其实就是插值器 getInterpolation 方法的返回值
startValue 表示动画的起始值
endValue 表示动画的结束值
return 返回值即为当前完成度下的所计算出来的值

接下来我们就来看系统内置的实现类 FloatEvaluator 的源码:

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

可以看到,FloatEvaluator所做的事就是简单地将结束值减去初始值乘以fraction后再加上初始值所得的结果进行返回,这跟我们平常计算比例值的方式是一样的。而IntEvaluator的实现也是差不多的,这两个类已经能够涵盖大部分动画所遇到的情况了,但是当我们遇到一些更加复杂的操作时,这两个类可能并不够用,接下来我们就来看如何自定义一个估值器。

2. 自定义估值器

在这里我会举两个例子来介绍自定义估值器的使用,首先我们先来看到第一个:自定义字符变化的估值器,将字符按照字母表的顺序进行过渡。这个需求比较简单,代码如下所示:

public class CharEvaluator implements TypeEvaluator<Character> {
    @Override
    public Character evaluate(float fraction, Character startValue, Character endValue) {
        return (char) (startValue + (endValue - startValue) * fraction);
    }
}

然后在Activity中,我们需要借助ValueAnimator.ofObject方法来使用自定义的估值器:

ValueAnimator anim = ValueAnimator.ofObject(new CharEvaluator(), 'A', 'Z');
anim.setDuration(500);
anim.setInterpolator(new LinearInterpolator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.d(TAG, "current character is " + animation.getAnimatedValue());
    }
});
anim.start();

为了能够观察到值的变化,我们为其添加监听,通过Log打印结果,结果如下所示:

D/MainActivity: current character is A
D/MainActivity: current character is A
D/MainActivity: current character is B
D/MainActivity: current character is C

D/MainActivity: current character is Y
D/MainActivity: current character is Z

可以看到成功地按照我们定义的方式从字符A过渡到了字符Z。当然这个自定义的估值器比较粗糙,仅做实验用,实际使用需要做更多改进!接下来我们来看一个在自定义View中应用的估值器,这个例子来自郭神的博客。

首先我们先自定义一个类Point表示坐标:

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 void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

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

接着我们为Point定义一个估值器PointEvaluator

public class PointEvaluator implements TypeEvaluator<Point> {
    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        float x = startValue.getX() + (endValue.getX() - startValue.getX()) * fraction;
        float y = startValue.getY() + (endValue.getY() - startValue.getY()) * fraction;
        return new Point(x, y);
    }
}

接着我们自定义一个View,如下所示:

public class CircleView extends View {

    private static final float RADIUS = 100.0f;

    private Point point;

    private Paint mPaint;

    public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    @Override
    protected void onDraw(Canvas canvas){
        if (point == null){
            point = new Point(RADIUS, RADIUS);
            drawCircle(canvas);
        } else {
            drawCircle(canvas);
        }
        invalidate();
    }

    private void drawCircle(Canvas canvas) {
        float x = point.getX();
        float y = point.getY();
        canvas.drawCircle(x, y, RADIUS, mPaint);
    }

    public Point getPoint(){
        return point;
    }

    public void setPoint(Point point){
        this.point = point;
    }
}

在这里需要特别说明2点:

  • 本例子中我们需要修改的是Point的属性值,所以在自定义的类中我们必须要提供相应的setPointgetPoint方法,因为属性动画是通过反射的原理来修改相应的属性值的。
  • 在重写onDraw方法的时候要特别注意调用invalidate方法。

接下来在Activity中,代码如下所示:

ObjectAnimator anim = ObjectAnimator.ofObject(circleView, "point",
        new PointEvaluator(),
        new Point(100.0f, 100.0f), new Point(500.0f, 500.0f));
anim.setDuration(2000);
anim.start();

For custom estimator, also called ofObjectthe method, here from the circle (100, 100) is moved to the position (500, 500) position. Run the code following effects:
Here Insert Picture Description
see custom Pointproperty estimators, we have successfully achieved the moving circle. These are all about the estimators. So far all of the contents of this article are finished presentation.


reference

Android attribute animation fully resolved (in), advanced usage of ValueAnimator and ObjectAnimator


I hope this article to help you ~

Guess you like

Origin blog.csdn.net/qq_38182125/article/details/89631460