深入理解 Android 動畫 Interpolator 類的使用

江南一點雨發表於2015-10-18

做過android動畫的人對Interpolator應該不會陌生,這個類主要是用來控制android動畫的執行速率,一般情況下,如果我們不設定,動畫都不是勻速執行的,系統預設是先加速後減速這樣一種動畫執行速率。

android通過Interpolator類來讓我們自己控制動畫的執行速率,還記得上一篇部落格中我們使用屬性動畫實現的旋轉效果嗎?在不設定Interpolator的情況下,這個動畫是先加速後減速,我們現在使用android系統提供的類LinearInterpolator來設定動畫的執行速率,LinearInterpolator可以讓這個動畫勻速執行,我們來看一個案例,我們有兩個TextView重疊放在一起,點選旋轉按鈕後這兩個TextView同時執行旋轉動畫,不同的是一個設定了LinearInterpolator,而另外一個什麼都沒有設定,程式碼如下:

LinearInterpolator ll = new LinearInterpolator();
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotation",
                    0f, 360f);
animator.setInterpolator(ll);
animator.setDuration(5000);
animator.start();
ObjectAnimator animator2 = ObjectAnimator.ofFloat(tv2, "rotation",
                    0f, 360f);
animator2.setDuration(5000);
animator2.start();

效果圖如下:

這裡寫圖片描述

現在我們可以很清楚的看到這裡的差異,一個TextView先加速後減速,一個一直勻速運動。

這就引起了我的好奇,究竟LinearInterpolator做了什麼,改變了動畫的執行速率。這裡我們就要看看原始碼了。

當我們呼叫animator.setInterpolator(ll);的時候,呼叫的是ValueAnimator方法中的setInterpolator方法,原始碼如下:

    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInterpolator = value;
        } else {
            mInterpolator = new LinearInterpolator();
        }
    }

我們看到這裡有一個mInterpolator變數,如果我們不執行這個方法,那麼mInterpolator 的預設值是多少呢?

我們找到了這樣兩行程式碼:

    // The time interpolator to be used if none is set on the animation
    private static final TimeInterpolator sDefaultInterpolator =
            new AccelerateDecelerateInterpolator();
private TimeInterpolator mInterpolator = sDefaultInterpolator;

這下明朗了,如果我們不設定,那麼系統預設使用AccelerateDecelerateInterpolator,AccelerateDecelerateInterpolator又是什麼呢?繼續看原始碼:

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

這裡的一個核心函式就是getInterpolation,使用了反餘弦函式,input傳入的值在0-1之間,因此這裡返回值的變化速率就是先增加後減少,對應的動畫執行速率就是先增加後減速。有興趣的童鞋可以使用MatLab來畫一下這個函式的影像。而當我們實現了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();
    }
}

這裡乾淨利落直接返回了input,沒有經過任何計算。input返回的值是均勻的,因此動畫得以勻速執行。

看到這裡,大家應該就明白了,如果我們想要控制動畫的執行速率,應該重寫getInterpolation方法就能實現。為了證實我們的猜想,我們繼續看原始碼。

大部分時候,我們使用的系統提供的各種各樣的**Interpolator,比如上文說的LinearInterpolator,這些類都是繼承自Interpolator,而Interpolator則實現了TimeInterpolator介面,我們來看看一個繼承結構圖:

這裡寫圖片描述

那麼這個終極大Boss TimeInterpolator究竟是什麼樣子呢?

package android.animation;

/**
 * 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,看來沒錯,就是它了,如果我們想要自定義Interpolator,只需要實現TimeInterpolator介面的getInterpolation方法就可以了,getInterpolation方法接收的引數是動畫執行的百分比,這個值是均勻的。

我們來個簡單的案例:

public class TanInterpolator implements TimeInterpolator {

    @Override
    public float getInterpolation(float t) {
        return (float) Math.sin((t / 2) * Math.PI);
    }
}

在動畫中使用:

TanInterpolator tl = new TanInterpolator();
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotation", 0f, 360f);
animator.setInterpolator(tl);
animator.setDuration(5000);
animator.start();

咦?這是什麼效果?這是一開始速度很大,然後逐漸減小到0的動畫效果.

原因如下:

看下圖,這是sin函式圖象:

這裡寫圖片描述

x取0-0.5PI,y值則為0-1,這一段曲線的斜率逐漸減小至0,這也是為什麼我們的動畫一開始執行很快,後來速度逐漸變為0.

好了,看完這些,想必大家已經理解了這個類的使用了吧。

相關文章