插值器(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è)置)
- 在動(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>
- 在 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()方法
- 補(bǔ)間動(dòng)畫實(shí)現(xiàn)Interpolator接口;屬性動(dòng)畫一般實(shí)現(xiàn)TimeInterpolator接口
- 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)置差值器:
- 勻速插值器:LinearInterpolator
- 先加速再減速 插值器: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,先減速后加速。
- 根據(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;
}
}
- 創(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

自定義插值器.gif
估值器(TypeEvaluator)
- 定義:一個(gè)接口
- 作用:設(shè)置屬性值從初始值過渡到結(jié)束值的變化具體數(shù)值
- 插值器(Interpolator)決定值的變化規(guī)律(勻速、加速等等),即決定的是變化趨勢(shì);而估值器決定數(shù)值的具體變化。
- 屬性動(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é))


