Android動(dòng)畫中篇(插值器、估值器)

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

插值器(Interpolator)

  • 定義:一個(gè)接口
  • 作用:設(shè)置屬性值從初始值過渡到結(jié)束值 的變化規(guī)律,如勻速、加速 & 減速等等
    ,即確定了 動(dòng)畫效果變化的模式,如勻速變化、加速變化等等。
  • 應(yīng)用場(chǎng)景:實(shí)現(xiàn)非線性運(yùn)動(dòng)的動(dòng)畫效果

非線性運(yùn)動(dòng):動(dòng)畫改變的速率不是一成不變的,如加速 & 減速運(yùn)動(dòng)都屬于非線性運(yùn)動(dòng)

  • 具體使用(在XML / Java代碼中設(shè)置)
  1. 在動(dòng)畫效果的XML代碼中設(shè)置插值器屬性android:interpolator
<?xml version="1.0" encoding="utf-8"?>
<!--通過資源ID設(shè)置插值器-->
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/overshoot_interpolator"
    android:duration="3000"
    android:startOffset="1000"

    android:fromXScale="0.0"
    android:toXScale="2"
    android:fromYScale="0.0"
    android:toYScale="2"
    android:pivotX="50%"
    android:pivotY="50%">
</scale>
  1. 在 Java 代碼中設(shè)置
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); 通過設(shè)置資源ID方式實(shí)現(xiàn),等同于下面創(chuàng)建對(duì)象方式
//創(chuàng)建對(duì)應(yīng)的插值器類對(duì)象
Interpolator overShootInterpolator = new OvershootInterpolator();
//給動(dòng)畫設(shè)置插值器
scaleAnimation.setInterpolator(overShootInterpolator);
//播放動(dòng)畫
button.startAnimation(scaleAnimation);
  • 那么使用插值器時(shí)的資源ID是什么呢?即有哪些類型的插值器可供我們使用呢?下面我們將介紹下系統(tǒng)給我們提供的內(nèi)置插值器類型。
  • 系統(tǒng)內(nèi)置插值器類型
作用 資源ID 對(duì)應(yīng)的Java類
動(dòng)畫加速進(jìn)行 @android:anim/accelerate_interpolator AccelerateInterpolator
快速完成動(dòng)畫,超出再回到結(jié)束樣式 @android:anim/overshoot_interpolator OvershootInterpolator
先加速再減速 @android:anim/accelerate_decelerate_interpolator AccelerateDecelerateInterpolator
先退后再加速前進(jìn) @android:anim/anticipate_interpolator AnticipateInterpolator
先退后再加速前進(jìn),超出終點(diǎn)后再回終點(diǎn) @android:anim/anticipate_overshoot_interpolator AnticipateOvershootInterpolator
最后階段彈球效果 @android:anim/bounce_interpolator BounceInterpolator
周期運(yùn)動(dòng) @android:anim/cycle_interpolator CycleInterpolator
減速 @android:anim/decelerate_interpolator DecelerateInterpolator
勻速 @android:anim/linear_interpolator LinearInterpolator
  • 使用時(shí):
    當(dāng)在XML文件設(shè)置插值器時(shí),只需傳入對(duì)應(yīng)的插值器資源ID即可
    當(dāng)在Java代碼設(shè)置插值器時(shí),只需傳入對(duì)應(yīng)的插值器資源ID或創(chuàng)建對(duì)應(yīng)的插值器對(duì)象即可

系統(tǒng)默認(rèn)的插值器是AccelerateDecelerateInterpolator,即先加速后減速

  • 系統(tǒng)內(nèi)置插值器的效果圖:


    系統(tǒng)內(nèi)置插值器.gif
  • 使用Android內(nèi)置的插值器能滿足大多數(shù)的動(dòng)畫需求,如果上述9個(gè)插值器無法滿足需求,還可以自定義插值器,下面將介紹如何自定義插值器(Interpolator)。
自定義插值器
  • 本質(zhì):根據(jù)動(dòng)畫的進(jìn)度(0%-100%)計(jì)算出當(dāng)前屬性值改變的百分比
  • 具體使用:自定義插值器需要實(shí)現(xiàn) Interpolator / TimeInterpolator接口 & 復(fù)寫getInterpolation()方法
  1. 補(bǔ)間動(dòng)畫實(shí)現(xiàn)Interpolator接口;屬性動(dòng)畫一般實(shí)現(xiàn)TimeInterpolator接口
  2. TimeInterpolator接口是屬性動(dòng)畫中新增的,用于兼容Interpolator接口,這使得所有過去的Interpolator實(shí)現(xiàn)類也可以直接在屬性動(dòng)畫使用。
// Interpolator接口
public interface Interpolator {  
     // 內(nèi)部只有一個(gè)方法
     /*參數(shù)說明:
     input值值變化范圍是0-1,且隨著動(dòng)畫進(jìn)度(0% - 100% )均勻變化;
     即動(dòng)畫開始時(shí),input值 = 0,動(dòng)畫結(jié)束時(shí)input = 1;
     而中間的值則是隨著動(dòng)畫的進(jìn)度(0% - 100%)在0到1之間均勻增加。*/
     float getInterpolation(float input) {  
      ...// 插值器的計(jì)算邏輯
      // 返回的值就是用于估值器繼續(xù)計(jì)算的fraction值,下面會(huì)詳細(xì)說明
      return xxx;
}  

// TimeInterpolator接口
// 同上
public interface TimeInterpolator {   
    float getInterpolation(float input);     
}
  • 在學(xué)習(xí)自定義插值器前,我們先來看兩個(gè)已經(jīng)實(shí)現(xiàn)好的系統(tǒng)內(nèi)置差值器:
  1. 勻速插值器:LinearInterpolator
  2. 先加速再減速 插值器:AccelerateDecelerateInterpolator
// 勻速差值器:LinearInterpolator
    @HasNativeInterpolator
    public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
        // 僅貼出關(guān)鍵代碼
        ...
        public float getInterpolation(float input) {
            return input;
            /*沒有對(duì)input值進(jìn)行任何邏輯處理,直接返回
             即input值 = fraction值
             因?yàn)閕nput值是勻速增加的,因此fraction值也是勻速增加的,
             所以動(dòng)畫的運(yùn)動(dòng)情況也是勻速的,所以是勻速插值器。*/
        }
    }

    // 先加速再減速 差值器:AccelerateDecelerateInterpolator
    @HasNativeInterpolator
    public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
        // 僅貼出關(guān)鍵代碼
        ...
        public float getInterpolation(float input) {
            return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
            /*input的運(yùn)算邏輯如下:
             使用了余弦函數(shù),因input的取值范圍是0到1,那么cos函數(shù)中的取值范圍就是π到2π。
             而cos(π)的結(jié)果是-1,cos(2π)的結(jié)果是1
             所以該值除以2加上0.5后,getInterpolation()方法最終返回的結(jié)果值還是在0到1之間。
             只不過經(jīng)過了余弦運(yùn)算之后,最終的結(jié)果不再是勻速增加的了,而是經(jīng)歷了一個(gè)先加速后減速的過程
             所以最終,fraction值 = 運(yùn)算后的值 = 先加速后減速,所以該差值器是先加速再減速的。*/
        }
    }
  • 從上面看出,自定義插值器的關(guān)鍵在于:對(duì)input值根據(jù)動(dòng)畫的進(jìn)度(0%-100%)通過邏輯計(jì)算,計(jì)算出當(dāng)前屬性值改變的百分比。
  • 下面我將用一個(gè)實(shí)例來說明該如何自定義插值器
實(shí)例
  • 目的:寫一個(gè)自定義Interpolator,先減速后加速。
  1. 根據(jù)需求實(shí)現(xiàn)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) {
            // 使用正弦函數(shù)來實(shí)現(xiàn)先減速后加速的功能,邏輯如下:
            // 因?yàn)檎液瘮?shù)初始弧度變化值非常大,剛好和余弦函數(shù)是相反的
            // 隨著弧度的增加,正弦函數(shù)的變化值也會(huì)逐漸變小,這樣也就實(shí)現(xiàn)了減速的效果。
            // 當(dāng)弧度大于π/2之后,整個(gè)過程相反了過來,現(xiàn)在正弦函數(shù)的弧度變化值非常小,漸漸隨著弧度繼續(xù)增加,變化值越來越大,弧度到π時(shí)結(jié)束,這樣從0過度到π,也就實(shí)現(xiàn)了先減速后加速的效果
            result = (float) (Math.sin(Math.PI * input)) / 2;
        } else {
            result = (float) (2 - Math.sin(Math.PI * input)) / 2;
        }
        // 返回的result值 = 隨著動(dòng)畫進(jìn)度呈先減速后加速的變化趨勢(shì)
        return result;
    }
}
  1. 創(chuàng)建自定義Interpolator對(duì)象并使用
                // 獲得當(dāng)前按鈕的位置
                float curTranslationX = button.getTranslationX();
                // 創(chuàng)建對(duì)象屬性動(dòng)畫,后面會(huì)詳細(xì)說明。
                /*參數(shù)表示的意思是:
                動(dòng)畫作用對(duì)象是button
                動(dòng)畫作用的對(duì)象的屬性是X軸平移
                動(dòng)畫效果是:從當(dāng)前位置平移到x=500的位置再平移到初始位置*/
                ObjectAnimator animator = ObjectAnimator.ofFloat(button, "translationX", curTranslationX, 500, curTranslationX);
                animator.setDuration(3000);
                //創(chuàng)建自定義插值器類對(duì)象
                TimeInterpolator decelerateAccelerateInterpolator = new DecelerateAccelerateInterpolator();
                //給動(dòng)畫設(shè)置自定義插值器
                animator.setInterpolator(decelerateAccelerateInterpolator);
                //播放動(dòng)畫
                animator.start();
效果圖
自定義插值器.gif

估值器(TypeEvaluator)

  • 定義:一個(gè)接口
  • 作用:設(shè)置屬性值從初始值過渡到結(jié)束值的變化具體數(shù)值
  1. 插值器(Interpolator)決定值的變化規(guī)律(勻速、加速等等),即決定的是變化趨勢(shì);而估值器決定數(shù)值的具體變化。
  2. 屬性動(dòng)畫特有的屬性。
  • 應(yīng)用場(chǎng)景:配合插值器一起使用,實(shí)現(xiàn)非線性運(yùn)動(dòng)的動(dòng)畫效果。
  • 具體使用
// 在第4個(gè)參數(shù)中傳入對(duì)應(yīng)估值器類的對(duì)象
ObjectAnimator anim = ObjectAnimator.ofObject(button, "height", new Evaluator(),1,3);
  • 系統(tǒng)內(nèi)置的估值器

IntEvaluator:以整型的形式從初始值 - 結(jié)束值 進(jìn)行過渡
FloatEvaluator:以浮點(diǎn)型的形式從初始值 - 結(jié)束值 進(jìn)行過渡
ArgbEvaluator:以Argb類型的形式從初始值 - 結(jié)束值 進(jìn)行過渡

  • 效果圖


    IntEvaluator.png

    FloatEvaluator.png
  • 如果上述內(nèi)置的估值器無法滿足需求,還可以自定義估值器,下面將介紹如何自定義插值器(Interpolator)。
自定義估值器
  • 本質(zhì):根據(jù)插值器計(jì)算出當(dāng)前屬性值改變的百分比結(jié)合初始值和結(jié)束值來計(jì)算當(dāng)前屬性具體的數(shù)值。

如:動(dòng)畫進(jìn)行了50%(初始值=100,結(jié)束值=200 ),那么勻速插值器計(jì)算出了當(dāng)前屬性值改變的百分比是50%,那么估值器則負(fù)責(zé)計(jì)算當(dāng)前屬性值 = 100 + (200-100)x50% = 150。

  • 具體使用:自定義估值器需要實(shí)現(xiàn) TypeEvaluator接口 & 復(fù)寫evaluate()
public interface TypeEvaluator {  
    // 參數(shù)說明
    // fraction:插值器getInterpolation()方法的返回值
    // startValue:動(dòng)畫的初始值
    // endValue:動(dòng)畫的結(jié)束值
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        ....// 估值器的計(jì)算邏輯
        return xxx;
        // 賦給動(dòng)畫屬性具體數(shù)值,使用反射機(jī)制改變屬性變化
    }  
}

特別注意:
插值器的input值和估值器fraction有什么關(guān)系呢?
答:input的值決定了fraction的值,input值經(jīng)過計(jì)算后傳入到插值器的getInterpolation()方法,然后通過實(shí)現(xiàn)getInterpolation()方法中的邏輯算法,根據(jù)input值來計(jì)算出一個(gè)返回值,而這個(gè)返回值就是fraction了。

  • 在學(xué)習(xí)自定義插值器前,我們先來看一個(gè)系統(tǒng)內(nèi)置差值器的實(shí)現(xiàn)——浮點(diǎn)型插值器(FloatEvaluator)。
// FloatEvaluator實(shí)現(xiàn)了TypeEvaluator接口
public class FloatEvaluator implements TypeEvaluator {  
    // 重寫evaluate()
    // 參數(shù)說明
    // fraction:表示動(dòng)畫完成度(根據(jù)它來計(jì)算當(dāng)前動(dòng)畫的值)
    // startValue、endValue:動(dòng)畫的初始值和結(jié)束值
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        float startFloat = ((Number) startValue).floatValue();  
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
        // 初始值過渡到結(jié)束值的算法是:
        // 1. 用結(jié)束值減去初始值,算出它們之間的差值
        // 2. 用上述差值乘以fraction系數(shù)
        // 3. 再加上初始值,就得到當(dāng)前動(dòng)畫的值
    }  
}
  • 屬性動(dòng)畫中的ValueAnimator.ofInt()&ValueAnimator.ofFloat()都具備系統(tǒng)內(nèi)置的估值器,即FloatEvaluator & IntEvaluator,即系統(tǒng)已經(jīng)默認(rèn)實(shí)現(xiàn)了如何從初始值過渡到結(jié)束值的邏輯。
  • 但對(duì)于ValueAnimator.ofObject(),從上面的工作原理可以看出并沒有系統(tǒng)默認(rèn)實(shí)現(xiàn),因?yàn)閷?duì)對(duì)象的動(dòng)畫操作復(fù)雜 & 多樣,系統(tǒng)無法知道如何從初始對(duì)象過度到結(jié)束對(duì)象。
  • 因此,對(duì)于ValueAnimator.ofObject(),我們需自定義估值器(TypeEvaluator)來告知系統(tǒng)如何進(jìn)行從初始對(duì)象過渡到結(jié)束對(duì)象的邏輯。
  • 自定義實(shí)現(xiàn)的邏輯如下
// 實(shí)現(xiàn)TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator{  
    // 復(fù)寫evaluate()
    // 在evaluate()里寫入對(duì)象動(dòng)畫過渡的邏輯
    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        // 參數(shù)說明
        // fraction:表示動(dòng)畫完成度(根據(jù)它來計(jì)算當(dāng)前動(dòng)畫的值)
        // startValue、endValue:動(dòng)畫的初始值和結(jié)束值
        ... // 寫入對(duì)象動(dòng)畫過渡的邏輯
        // 返回對(duì)象動(dòng)畫過渡的邏輯計(jì)算后的值
        return value;  
    }
  • 實(shí)例將在下篇值屬性動(dòng)畫的ValueAnimator.ofObject中詳細(xì)說明。

感謝

Android 動(dòng)畫:你真的會(huì)使用插值器與估值器嗎?(含詳細(xì)實(shí)例教學(xué))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容