Android 动画之属性动画- Interpolator(内插器)

本文深入探讨了Android属性动画中的Interpolator,解释了内插器如何影响动画执行速率。通过源码分析,展示了LinearInterpolator和AccelerateDecelerateInterpolator的工作原理,并详细介绍了自定义Interpolator的过程,解释了为何某些动画会出现倒转效果。通过对Interpolator和TypeEvaluator的结合使用,读者可以更好地掌握动画的定制技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android动画系列:

介绍

对于Interpolator(内插器),写过动画的都不会陌生,其本身看做是一个时间计算器,用于定义动画的执行的速率。

public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {

     ***            

    // The time interpolator to be used if none is set on the animation
    private static final TimeInterpolator sDefaultInterpolator =
            new AccelerateDecelerateInterpolator();
    /**
     * The time interpolator to be used. The elapsed fraction of the animation will be passed
     * through this interpolator to calculate the interpolated fraction, which is then used to
     * calculate the animated values.
     */
    private TimeInterpolator mInterpolator = sDefaultInterpolator;

    ***  

}

上面是ValueAnimator源码的一部分,在这里可以清晰的看出来,在我们不为ValueAnimator设置Interpolator为AccelerateDecelerateInterpolator,也就是说系统默认是先加速后减速这样一种动画执行速率。

    LinearInterpolator ll = new LinearInterpolator();
    ObjectAnimator animator = ObjectAnimator.ofFloat(btnProperty, "rotation",
            0f, 360f);
    animator.setInterpolator(ll);
    animator.setDuration(5000);
    animator.setRepeatCount(ValueAnimator.INFINITE);

    ObjectAnimator animatorA = ObjectAnimator.ofFloat(btnPropertyA, "rotation",
            0f, 360f);

    animatorA.setRepeatCount(ValueAnimator.INFINITE);
    animatorA.setDuration(5000);

    animator.start();
    animatorA.start();

这里写图片描述

先定义两个Button,其中,btnProperty背景色设置为红色,而btnPropertyA的背景色设置为蓝色.对两个Button分别定义了动画,btnProperty的动画的Interpolator设置为LinearInterpolator,即动画的执行速率为匀速运动,而btnPropertyA的动画的Interpolator设置未设置使用的是默认的。从实际运行的效果可以清晰的看出,刚开始时,btnProperty动画运行速率快于btnPropertyA,随着动画的执行,btnPropertyA逐渐追上了btnProperty,在同一轮动画时,它们同时处于结束位置。对于动画的默认Interpolator为AccelerateDecelerateInterpolator,也就有了合理的解释了。

源码分析

对于动画的执行速率,Interpolator是怎么计算的呢?先看Interpolator的源码:

public interface Interpolator extends 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.
     */
    float getInterpolation(float input);
}

从Interpolator的源码可以看出,Interpolator其实就是一个空的接口,继承与TimeInterpolator。再看看TimeInterpolator这个接口,其内只声明了getInterpolation(float)这一个方法。传入getInterpolation(float)方法的参数实际上是动画的进度,其值在0和1之间,0表示动画开始,1表示动画结束。而其返回值是表示内插器计算出来的值,其可以小于0或者大于1。然后将内插值乘以动画中属性值的差量即为画的当前属性值。

刚才我们用到了LinearInterpolator,表示动画匀速运行,下面看看匀速效果是怎么现实的。

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

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

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

从LinearInterpolator源码中可以看出,在getInterpolation(float input)方法中并没有对input值做任何处理。由于input值是动画执行的百分比,这个值是均匀的。故而LinearInterpolator计算出的内插值其实就是动画执行的百分比,没有啥变化。

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

对于AccelerateDecelerateInterpolator而言,其getInterpolation(input)方法中对intput做了处理,下图为其处理方式所对应的函数图像,从图像可以清晰的看出,内插值开始时增长趋势很快,然后在减小。相对应内插值计算动画属性时,应该是起初动画属性值逐渐增大很快,然后增大趋势减弱,意味着表现出来的动画效果为先加速再减速。

这里写图片描述

自定义Interpolator

1.自定义一个Interpolator,命名为MyInterpolator

public class MyInterpolator implements TimeInterpolator {
    @Override
    public float getInterpolation(float input) {
        float inter;
        inter = 1 - input * input;
        return inter;
    }
}animatorA

2.将animator的Interpolator设置为LinearInterpolator,而animatorA的设置为刚定义的MyInterpolator。

    LinearInterpolator ll = new LinearInterpolator();
    ObjectAnimator animator = ObjectAnimator.ofFloat(btnProperty, "rotation",
            0f, 360f);
    animator.setInterpolator(ll);
    animator.setDuration(5000);
    animator.setRepeatCount(ValueAnimator.INFINITE);

    ObjectAnimator animatorA = ObjectAnimator.ofFloat(btnPropertyA, "rotation",
            0f, 360f);
    animatorA.setInterpolator(new MyInterpolator());
    animatorA.setRepeatCount(ValueAnimator.INFINITE);
    animatorA.setDuration(5000);

    animator.start();
    animatorA.start();

这里写图片描述

从效果图上,可以看出btnPropertyA的实际动画效果是倒着转的,而且是先加速后减速,为啥会出现这种情况呢?百思不得其解,前面已经说过btnPropertyA的动画的内插器为MyInterpolator,那么此种效果必然与内插器有关。先不管效果到底是为啥,先MyInterpolator中,从1 - input * input的函数图像和实际值,来看看getInterpolation(input)的处理结果。

  1. 函数图像
    下图为在0-1之间的函数图像,从函数图像可以看出,其函数值先加速后减速。

这里写图片描述

  1. 实际值
inputgetInterpolation
01
0.10.99
0.20.96
0.40.86
0.60.64
0.80.36
0.90.19
1.00

从函数图像和实际值的差值,我们清楚的看到,动画属性值的变化幅度先逐渐变大然后再逐渐减少的,这也能够解释动画的执行速率问题了。

接下来看看动画为啥倒转呢。在声明动画时,开始旋转角度为0f,结束的旋转角度为360.0f,也就是动画的实际效果是旋转一周,这个没有问题,实际上也就是选择一周的。而动画的旋转角度差值为360.0f。假如动画的执行百分比为0.1时,MyInterpolator内插器计算出来的内插值为0.99f.
前面说过将内插值乘以动画中属性值的差量即为画的当前属性值,此时动画当前的属性值应为356.4f,也就是此时,btnPropertyA旋转了356.4。同理当动画执行百分比为0.2时,btnPropertyA旋转了345.6度。也就是btnPropertyA的动画效果实际上是先旋转到360度,然后慢慢旋转到0度(初始位置)。这也就是btnPropertyA的动画倒转的原因。

也就是说动画的属性值的计算是由Interpolator和Evaluators共同计算出来的。当动画开始以后,Interpolator先获取动画执行的百分比并计算出来内插值,Evaluators在获取Interpolator计算出的内差值并在evaluate(float fraction, MyPoint pointStart, MyPoint pointEnd)内计算出动画的属性值。有说过fraction为动画的执行的百分比,其实描述的并不准确,其实际值因为Interpolator计算出的内插值。既然了解到了Interpolator、Evaluators和动画属性值之间的计算关系,以后随心所欲的自定义动画了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值