您当前的位置: 首页 >  动画

Android技术栈

暂无认证

  • 0浏览

    0关注

    111博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

详解 Android 属性动画中的插值器:“Interpolator”

Android技术栈 发布时间:2022-07-11 19:00:45 ,浏览量:0

插值器Interpolator

插值器(Interpolator)用于定义动画随时间流逝的变化规律; 这句话说起来比较抽象,但其实在我们实际使用属性动画的时候,我们能明显感觉到插值器的作用

例如默认采用的加减速插值器,它会在动画开始执行逐渐加速,然后又逐渐减速直至动画结束;而线性插值器则是按着一个均匀的速度执行完整个动画的 加减速插值器先逐渐加速、再逐渐减速,而线性插值器一直保持恒定的速率执行完成。在知道了插值器的作用之后,接下来我们就来看Android中有哪些内置的插值器。

系统内置插值器

Android系统中共内置了9种插值器,如下表所示:

那么我们来看下插值器的相关接口以及如何自定义一个插值器

自定义插值器我们需要实现 Interpolator / TimeInterpolator接口并实现接口方法getInterpolation。接下来看到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;
 }
 ......
}

可以看到,它就是简单地将输入参数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);
 }
 }

 ......
}
 //Math.pow(底数,几次方)
 //int c=(int)Math.pow(3,3);
 //c=27;

在默认情况下,mFactor的值为1.0f,也就是说它的返回值是input*input; 结合一定的数学知识和上面的相关图像我们很容易得知,在0~1这个区间上斜率是逐渐增加的,符合加速度插值器的特点

估值器TypeEvaluator

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

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

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

TypeEvaluator的接口定义如下:

public interface TypeEvaluator {

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

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

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

public class FloatEvaluator implements TypeEvaluator {

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

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

自定义估值器

在这里我会举两个例子来介绍自定义估值器的使用,首先我们先来看到第一个: 自定义字符变化的估值器,将字符按照字母表的顺序进行过渡;

这个需求比较简单,代码如下所示:

public class CharEvaluator implements TypeEvaluator {
 @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();

接下来我们来看一个在自定义View中应用的估值器

  1. 首先先自定义一个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;
    }
}

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

public class PointEvaluator implements TypeEvaluator {
    @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);
    }
}

3.接着我们自定义一个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点:

  1. 本例子中我们需要修改的是Point的属性值,所以在自定义的类中我们必须要提供相应的setPoint和getPoint方法,因为属性动画是通过反射的原理来修改相应的属性值的

  2. 在重写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();
插值器&估值器

插值器:

概念: 根据 时间流失的百分比 计算 当前属性改变的百分比

场景: 实现非线性运动的动画效果

估值器:

概念: 根据 当前属性改变的百分比 计算 改变后的属性值

插值器决定属性值随时间变化的规律;而具体变化属性数值则交给估值器去计算

二者关系:

属性动画是对属性做动画,属性要实现动画

1.首先由插值器根据时间流逝的百分比计算出当前属性值改变的百分比,然后由插值器将这个百分比返回;这个时候插值器的工作就完成了

比如 插值器 返回的值是0.5,很显然我们要的不是0.5

2.插值器计算好属性变化百分比后,由估值器根据当前属性改变的百分比来计算改变后的属性值,根据这个属性值,我们就可以对View设置当前的属性值了

好了,文章基本上就到这里,如有地方不对或者有不同理解的可以提出来

有需要文中完整代码的同学 可以在评论区下方留言 “底层源码” 或者私信我 即可 免费获取

最后我想说:

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

Android 架构师之路还很漫长,与君共勉

关注
打赏
1665219970
查看更多评论
立即登录/注册

微信扫码登录

0.0377s