Android动画中篇(插值器、估值器)

插值器(Interpolator)&估值器(TypeEvaluator) 插值器(Interpolator)

  • 定义:一个接口
  • 作用:设置属性值从初始值过渡到结束值 的变化规律,如匀速、加速 & 减速等等
    ,即确定了 动画效果变化的模式,如匀速变化、加速变化等等。
  • 应用场景:实现非线性运动的动画效果
非线性运动:动画改变的速率不是一成不变的,如加速 & 减速运动都属于非线性运动
  • 具体使用(在XML / Java代码中设置)
  1. 在动画效果的XML代码中设置插值器属性android:interpolator

  1. 在 Java 代码中设置
Animation scaleAnimation = new ScaleAnimation(0, 2, 0, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnimation.setDuration(3000); //scaleAnimation.setInterpolator(MainActivity.this,android.R.anim.overshoot_interpolator); 通过设置资源ID方式实现,等同于下面创建对象方式 //创建对应的插值器类对象 Interpolator overShootInterpolator = new OvershootInterpolator(); //给动画设置插值器 scaleAnimation.setInterpolator(overShootInterpolator); //播放动画 button.startAnimation(scaleAnimation);

  • 那么使用插值器时的资源ID是什么呢?即有哪些类型的插值器可供我们使用呢?下面我们将介绍下系统给我们提供的内置插值器类型。
  • 系统内置插值器类型
作用 资源ID 对应的Java类
动画加速进行 @android:anim/accelerate_interpolator AccelerateInterpolator
快速完成动画,超出再回到结束样式 @android:anim/overshoot_interpolator OvershootInterpolator
先加速再减速 @android:anim/accelerate_decelerate_interpolator AccelerateDecelerateInterpolator
先退后再加速前进 @android:anim/anticipate_interpolator AnticipateInterpolator
先退后再加速前进,超出终点后再回终点 @android:anim/anticipate_overshoot_interpolator AnticipateOvershootInterpolator
最后阶段弹球效果 @android:anim/bounce_interpolator BounceInterpolator
周期运动 @android:anim/cycle_interpolator CycleInterpolator
减速 @android:anim/decelerate_interpolator DecelerateInterpolator
匀速 @android:anim/linear_interpolator LinearInterpolator
  • 使用时:
    当在XML文件设置插值器时,只需传入对应的插值器资源ID即可
    当在Java代码设置插值器时,只需传入对应的插值器资源ID或创建对应的插值器对象即可
系统默认的插值器是AccelerateDecelerateInterpolator,即先加速后减速
  • 系统内置插值器的效果图:

    Android动画中篇(插值器、估值器)
    文章图片
    系统内置插值器.gif
  • 使用Android内置的插值器能满足大多数的动画需求,如果上述9个插值器无法满足需求,还可以自定义插值器,下面将介绍如何自定义插值器(Interpolator)。
自定义插值器
  • 本质:根据动画的进度(0%-100%)计算出当前属性值改变的百分比
  • 具体使用:自定义插值器需要实现 Interpolator / TimeInterpolator接口 & 复写getInterpolation()方法
  1. 补间动画实现Interpolator接口;属性动画一般实现TimeInterpolator接口
  2. TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类也可以直接在属性动画使用。
// Interpolator接口 public interface Interpolator { // 内部只有一个方法 /*参数说明: input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化; 即动画开始时,input值 = 0,动画结束时input = 1; 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加。*/ float getInterpolation(float input) { ...// 插值器的计算逻辑 // 返回的值就是用于估值器继续计算的fraction值,下面会详细说明 return xxx; }// TimeInterpolator接口 // 同上 public interface TimeInterpolator { float getInterpolation(float input); }

  • 在学习自定义插值器前,我们先来看两个已经实现好的系统内置差值器:
  1. 匀速插值器:LinearInterpolator
  2. 先加速再减速 插值器:AccelerateDecelerateInterpolator
// 匀速差值器:LinearInterpolator @HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { // 仅贴出关键代码 ... public float getInterpolation(float input) { return input; /*没有对input值进行任何逻辑处理,直接返回 即input值 = fraction值 因为input值是匀速增加的,因此fraction值也是匀速增加的, 所以动画的运动情况也是匀速的,所以是匀速插值器。*/ } }// 先加速再减速 差值器:AccelerateDecelerateInterpolator @HasNativeInterpolator public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory { // 仅贴出关键代码 ... public float getInterpolation(float input) { return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; /*input的运算逻辑如下: 使用了余弦函数,因input的取值范围是0到1,那么cos函数中的取值范围就是π到2π。 而cos(π)的结果是-1,cos(2π)的结果是1 所以该值除以2加上0.5后,getInterpolation()方法最终返回的结果值还是在0到1之间。 只不过经过了余弦运算之后,最终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程 所以最终,fraction值 = 运算后的值 = 先加速后减速,所以该差值器是先加速再减速的。*/ } }

  • 从上面看出,自定义插值器的关键在于:对input值根据动画的进度(0%-100%)通过逻辑计算,计算出当前属性值改变的百分比。
  • 下面我将用一个实例来说明该如何自定义插值器
实例
  • 目的:写一个自定义Interpolator,先减速后加速。
  1. 根据需求实现Interpolator接口
    DecelerateAccelerateInterpolator.java
package cn.lq.com.blog; import android.animation.TimeInterpolator; public class DecelerateAccelerateInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { float result; if (input <= 0.5) { // 使用正弦函数来实现先减速后加速的功能,逻辑如下: // 因为正弦函数初始弧度变化值非常大,刚好和余弦函数是相反的 // 随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。 // 当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果 result = (float) (Math.sin(Math.PI * input)) / 2; } else { result = (float) (2 - Math.sin(Math.PI * input)) / 2; } // 返回的result值 = 随着动画进度呈先减速后加速的变化趋势 return result; } }

  1. 创建自定义Interpolator对象并使用
// 获得当前按钮的位置 float curTranslationX = button.getTranslationX(); // 创建对象属性动画,后面会详细说明。 /*参数表示的意思是: 动画作用对象是button 动画作用的对象的属性是X轴平移 动画效果是:从当前位置平移到x=500的位置再平移到初始位置*/ ObjectAnimator animator = ObjectAnimator.ofFloat(button, "translationX", curTranslationX, 500, curTranslationX); animator.setDuration(3000); //创建自定义插值器类对象 TimeInterpolator decelerateAccelerateInterpolator = new DecelerateAccelerateInterpolator(); //给动画设置自定义插值器 animator.setInterpolator(decelerateAccelerateInterpolator); //播放动画 animator.start();

效果图 Android动画中篇(插值器、估值器)
文章图片
自定义插值器.gif 估值器(TypeEvaluator)
  • 定义:一个接口
  • 作用:设置属性值从初始值过渡到结束值的变化具体数值
  1. 插值器(Interpolator)决定值的变化规律(匀速、加速等等),即决定的是变化趋势;而估值器决定数值的具体变化。
  2. 属性动画特有的属性。
  • 应用场景:配合插值器一起使用,实现非线性运动的动画效果。
  • 具体使用
// 在第4个参数中传入对应估值器类的对象 ObjectAnimator anim = ObjectAnimator.ofObject(button, "height", new Evaluator(),1,3);

  • 系统内置的估值器
IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡
  • 效果图

    Android动画中篇(插值器、估值器)
    文章图片
    IntEvaluator.png
    Android动画中篇(插值器、估值器)
    文章图片
    FloatEvaluator.png
  • 如果上述内置的估值器无法满足需求,还可以自定义估值器,下面将介绍如何自定义插值器(Interpolator)。
自定义估值器
  • 本质:根据插值器计算出当前属性值改变的百分比结合初始值和结束值来计算当前属性具体的数值。
如:动画进行了50%(初始值=100,结束值=200 ),那么匀速插值器计算出了当前属性值改变的百分比是50%,那么估值器则负责计算当前属性值 = 100 + (200-100)x50% = 150。
  • 具体使用:自定义估值器需要实现 TypeEvaluator接口 & 复写evaluate()
public interface TypeEvaluator { // 参数说明 // fraction:插值器getInterpolation()方法的返回值 // startValue:动画的初始值 // endValue:动画的结束值 public Object evaluate(float fraction, Object startValue, Object endValue) { ....// 估值器的计算逻辑 return xxx; // 赋给动画属性具体数值,使用反射机制改变属性变化 } }

特别注意:
插值器的input值和估值器fraction有什么关系呢?
答:input的值决定了fraction的值,input值经过计算后传入到插值器的getInterpolation()方法,然后通过实现getInterpolation()方法中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了。
  • 在学习自定义插值器前,我们先来看一个系统内置差值器的实现——浮点型插值器(FloatEvaluator)。
// FloatEvaluator实现了TypeEvaluator接口 public class FloatEvaluator implements TypeEvaluator { // 重写evaluate() // 参数说明 // fraction:表示动画完成度(根据它来计算当前动画的值) // startValue、endValue:动画的初始值和结束值 public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); // 初始值过渡到结束值的算法是: // 1. 用结束值减去初始值,算出它们之间的差值 // 2. 用上述差值乘以fraction系数 // 3. 再加上初始值,就得到当前动画的值 } }

  • 属性动画中的ValueAnimator.ofInt()&ValueAnimator.ofFloat()都具备系统内置的估值器,即FloatEvaluator & IntEvaluator,即系统已经默认实现了如何从初始值过渡到结束值的逻辑。
  • 但对于ValueAnimator.ofObject(),从上面的工作原理可以看出并没有系统默认实现,因为对对象的动画操作复杂 & 多样,系统无法知道如何从初始对象过度到结束对象。
  • 因此,对于ValueAnimator.ofObject(),我们需自定义估值器(TypeEvaluator)来告知系统如何进行从初始对象过渡到结束对象的逻辑。
  • 自定义实现的逻辑如下
// 实现TypeEvaluator接口 public class ObjectEvaluator implements TypeEvaluator{ // 复写evaluate() // 在evaluate()里写入对象动画过渡的逻辑 @Override public Object evaluate(float fraction, Object startValue, Object endValue) { // 参数说明 // fraction:表示动画完成度(根据它来计算当前动画的值) // startValue、endValue:动画的初始值和结束值 ... // 写入对象动画过渡的逻辑 // 返回对象动画过渡的逻辑计算后的值 return value; }

  • 实例将在下篇值属性动画的ValueAnimator.ofObject中详细说明。
感谢
【Android动画中篇(插值器、估值器)】Android 动画:你真的会使用插值器与估值器吗?(含详细实例教学)

    推荐阅读