Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解

特雷西多士發表於2017-03-29

寶劍鋒從磨礪出,梅花香自苦寒來;千淘萬漉雖辛苦,吹盡狂沙始到金; 長風破浪會有時,直掛雲帆濟滄海

歡迎大家想看更多關於Android基礎夯實系列博文,請移步到我的部落格:
Ryane's Blog

一、摘要

Animator類作為屬性動畫的基類,它是一個抽象類,它提供了實現動畫的基本架構,但是我們不能直接使用它,因為它只是提供了最基本的的實現動畫的方法,只有讓它的子類繼承它並進行相應擴充套件之後,我們才會使用它實現動畫。在屬性動畫中,Animator包括了ValueAnimator、ObjectAnimator和AnimatorSet三個子類,下面給大家詳解ValueAnimator。

如果你想了解更權威的解釋,可以檢視官方文件:Property Animation

本文主要對ValueAnimator做介紹,如果大家有興趣,可以繼續閱讀本動畫系列其他相關文章,作者也在不斷更新完善相關內容,希望大家可以指出有誤之處。

Android基礎夯實--重溫動畫(一)之Tween Animation

Android基礎夯實--重溫動畫(二)之Frame Animation

Android基礎夯實--重溫動畫(三)之初識Property Animation

二、ValueAnimator

ValueAnimator,就是針對值的,也就是說ValueAnimator不會對控制元件進行任何操作,而是控制值的變化,然後我們監聽這個值的變化過程,自己來控制控制元件的變化。什麼意思呢?就像我們上面1.2中的例子,使用屬性動畫來控制TextView的位移,我們在初始化ValueAnimator時,會設定一個初始值和結束的值,例子我用這兩個值來控制TextView在y軸上的位置,然後設定監聽器,監聽初始值變化到結束值的過程,在不斷變化過程中,通過呼叫TextView的layout方法來不斷更新TextView的位置,從而實現位移動畫。

2.1 初識ValueAnimator

先上一個例子,實現圖片的漸變過程:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
Alpha

我們都知道,在使用Tween Animation時是非常容易實現的,使用AlphaAnimation就可以實現,假如我們用屬性動畫的話,怎麼實現呢?也是非常簡單,佈局程式碼就不貼了,看看使用ValueAnimator如何簡單快捷地實現漸變動畫。

// 第一步,建立一個ValueAnimator。直接呼叫ValueAnimator.ofFloat來初始化,設定開始值和結束值
final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1, 0);
// 設定變化時長
alphaAnimator.setDuration(1000);
alphaAnimator.start();

// 第二步,ValueAnimator設定監聽器
alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        // 我們來檢查一下這個方法會呼叫多少次
        Log.i("TAG", "curValue is " + animation.getAnimatedValue());
        // 在ValueAnimator變化的過程中更新控制元件的透明度
        mBinding.image.setAlpha((float)alphaAnimator.getAnimatedValue());
    }
});複製程式碼

而我們在監聽器內設定的Log的結果如下,我們可以看到onAnimationUpdate方法被不斷地執行,輸出值不斷由我們設定的初始值變化到我們設定的結束值,所以這個值的變化過程正是我們需要讓控制元件變化的過程。

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
Log結果

通過例子,我們可以大概總結使用ValueAnimator的兩個主要過程:

(1). 初始化ValueAnimator,並設定初始值和結束值,還有動畫的時間,然後start。

(2). 給ValueAnimator設定監聽器,通過getAnimatedValue()拿到變化值,然後我們手動更新控制元件的變化。

2.2 深入瞭解ValueAnimator

由於ValueAnimator裡面的方法確實不少,所以我們從上面的例子入手,從常用到不常用地講解ValueAnimator的API,畢竟只要我們掌握了最常用的知識點之後,在我們需要時再去深入瞭解不常用的知識點,我覺得是個最有效率的學習方式。

由上面的Demo程式碼的第一步我們可以看到,首先我們需要獲取到一個ValueAnimator例項,按照我們的常規思維,我們都會通過new一個物件出來,以程式碼為例,我們是通過ofFloat方法來獲取一個例項物件,那麼我們的第一個疑問就是關於建構函式的,到底ValueAnimator有沒有建構函式呢?如果有,為什麼不通過建構函式來初始化呢?

答案是有的,至於為什麼,我們一探究竟。

2.2.1建構函式

  • ValueAnimator():建立一個ValueAnimator物件。

ValueAnimator確實有它的建構函式,但是官方文件不建議我們直接使用它,因為在內部實現的時候才會用到它。之所以不需要用到它,是因為API給我們封裝了一系列的的方法來獲取例項物件。

2.2.2例項化物件的方法

  • ValueAnimator ofInt (int... values):返回一個int型變化的ValueAnimator。
  • ValueAnimator ofFloat (float... values):返回一個float型變化的ValueAnimator。
  • ValueAnimator ofObject (TypeEvaluator evaluator, Object... values):返回一個object型變化的ValueAnimator。
  • ValueAnimator ofArgb (int... values):返回一個顏色值變化的ValueAnimator,API LEVEL 21引入。
  • ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values):返回一個PropertyValuesHolder型變化的ValueAnimator,在ObjectAnimator再詳說。

為什麼我們需要通過這些方法來例項化物件呢?這是因為這些方法內部都對例項化物件進行了封裝,我們以ofInt為例看一下它的內部實現,它內部其實還是通過new的方式來例項化,然後通過設定一些屬性,然後返回這個ValueAnimator物件。

public static ValueAnimator ofInt(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    return anim;
}複製程式碼

ofArgb的使用

在ValueAnimator中的ofArgb()可以幫助我們實現顏色的漸變效果,Google在API LEVEL 21之後增加了這個方法ofArgb()。通過這個方法我們更容易地實現顏色演變,通過ofArgb和ArgbEvaluator,我們可以輕鬆實現顏色漸變效果:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
ofInt

程式碼:

ValueAnimator animator = ValueAnimator.ofInt(0xffff00ff, 0xffffff00, 0xffff00ff);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(3000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mBinding.image.setBackgroundColor((Integer) animation.getAnimatedValue());
    }
});複製程式碼

ofObject的使用

ofObject方法是什麼意思呢?我們都知道ofInt和ofFloat都是針對Int值和Float值的變化,但是,我們只能控制一個值的變化,但是當我們需要實現多值變化時,它們就不再滿足我們的需求。例如我們需要同時實現位移、透明度變化等動畫,這裡需要設定兩個屬性值的變化,所以如果我們只有一個初始值是不行的,因為兩個屬性的初始值和結束值不一樣,那麼我們就可以將兩個屬性值封裝到一個物件裡面,那麼初始值的object和結束值的object就可以包含兩個屬性不同的初始值和結束值了。

下面是一個對ValueAnimator ofObject (TypeEvaluator evaluator, Object... values)方法具體使用的小Demo,實現圖片的放大和漸變過程,先看效果圖:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
image

首先我們看到ofObject的引數裡面有一個TypeEvaluator和一個Object型可變引數,一般傳入一個初始值和結束值,首先TypeEvaluator就是一個計算值的工具,API提供有現成的(下面詳說),也可以自己實現(這裡為了給大家知道是個什麼東西,就自己實現);然後Obejct型,我們自己寫一個類代替Obejct型。

因為我們有兩個動畫,包括放大和透明度變化,我們定義一個叫ValueObject的類,裡面就包含兩個屬性,程式碼也非常簡單:

class ValueObject {
    float alphaValue;   //透明度的值
    float scaleValue;   //伸縮變化的值

    public ValueObject(float alphaValue, float scaleValue) {
        this.alphaValue = alphaValue;
        this.scaleValue = scaleValue;
    }
}複製程式碼

然後,我們就需要自定義TypeEvaluator了,因為TypeEvaluator是一個介面,我們就寫一個名叫MyEvaluator的類,它實現了TypeEvaluator的介面,傳入我們的值型別為ValueObject,然後重寫evaluate方法(TypeEvaluator介面只有這個方法需要實現),程式碼也很簡單:

class MyEvaluator implements TypeEvaluator<ValueObject> {

    // 屬性動畫封裝了一個因子fraction,我們設定動畫時需要setDuration(xxxx),例如時間為1000ms,那麼當到達100ms時,fraction就為0.1
    // fraction也就是當前時間佔總時間的百分比,startValue和endValue就是我們傳入的初始值和結束值
    @Override
    public ValueObject evaluate(float fraction, ValueObject startValue, ValueObject endValue) {
        // 計算某個時刻的alpha值和scale值。類似速度公式Vt = V0 + at
        float nowAlphaValue = startValue.alphaValue + (endValue.alphaValue - startValue.alphaValue) * fraction;
        float nowScaleValue = startValue.scaleValue + (endValue.scaleValue - startValue.scaleValue) * fraction;
        return new ValueObject(nowAlphaValue, nowScaleValue);
    }
}複製程式碼

這兩個類我們都實現了,那麼動畫就很簡單了:

public void objectAnimation() {
    // 初始alpha值為1,scale值為1
    ValueObject startObjectVal = new ValueObject(1f, 1f);
    // 結束alpha值為0,scale值為2,相當於透明度變為0,尺寸放大到2倍
    ValueObject endObjectVal = new ValueObject(0f, 2f);
    MyEvaluator myEvaluator = new MyEvaluator();

    final ValueAnimator animator = ValueAnimator.ofObject(myEvaluator, startObjectVal, endObjectVal);
    animator.setDuration(3000);
    animator.start();

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBinding.image.setAlpha(((ValueObject) animation.getAnimatedValue()).alphaValue);
            mBinding.image.setScaleType(ImageView.ScaleType.CENTER);
            mBinding.image.setScaleX(((ValueObject) animation.getAnimatedValue()).scaleValue);
            mBinding.image.setScaleY(((ValueObject) animation.getAnimatedValue()).scaleValue);
        }
    });
}複製程式碼

2.2.3常用方法

  • void addUpdateListener(ValueAnimator.AnimatorUpdateListener listener):新增值變化監聽器。主要監聽值變化,實現動畫。
  • void addUpdateListener(AnimatorUpdateListener listener):新增動畫狀態監聽器。重寫動畫開始、結束、取消、重複四個方法,監聽不同狀態。
  • void cancel (): 取消動畫。
  • void end ():讓動畫到達最後一幀。
  • void start():開始動畫。
  • void pause():暫停動畫。
  • void resume():繼續動畫。
  • void reverse ():反向播放動畫。
  • boolean isRunning():是否在執行中。
  • boolean isStarted():是否已經開始。

2.2.4屬性相關的方法

  • void setCurrentFraction(float fraction):設定當前時間因子。即時間到達的百分比。
  • float getAnimatedFraction():獲取當前時間因子。即時間到達的百分比。
  • void setCurrentPlayTime (long playTime):設定當前的時間,取值為0-duration,單位毫秒。
  • long getCurrentPlayTime ():獲取當前的時間,單位毫秒。
  • ValueAnimator setDuration (long duration):設定動畫總時長,單位毫秒。
  • long getDuration ():獲取動畫總時長,單位毫秒。
  • void setFrameDelay (long frameDelay):設定每一幀之間間隔多少毫秒。
  • long getFrameDelay ():獲取每一幀之間間隔多少毫秒。
  • void setInterpolator (TimeInterpolator value):設定動畫的Interpolator,和View Animation的Interpolator通用。
  • TimeInterpolator getInterpolator ():獲取當前使用的插值器。
  • void setRepeatCount(int value):設定重複次數。
  • int getRepeatCount():獲取重複次數。
  • void setRepeatMode(int value):設定重複模式。有RESTART和REVERSE兩種。
  • int getRepeatMode():獲取重複模式。
  • void setStartDelay(long startDelay):設定開始前延遲毫秒數。
  • long getStartDelay():獲取開始前延遲毫秒數。

  • void getAnimatedValue():獲取計算出來的當前屬性值。

  • getAnimatedValue(String propertyName):獲取計算出來的當前某個屬性的值。
  • void setEvaluator(TypeEvaluator value):設定求值器。
  • void setFloatValues(float... values):設定Float型變化值,一般設定初始值和結束值,當然你也可以設定中間值,因為這是一個可變引數,長度可變。
  • void setIntValues(int... values):設定Int型變化值,一般設定初始值和結束值,當然你也可以設定中間值,因為這是一個可變引數,長度可變。
  • setObjectValues(Object... values):設定Object型變化值,一般設定初始值和結束值,當然你也可以設定中間值,因為這是一個可變引數,長度可變。

2.2.5監聽器

ValueAnimator有兩個監聽器,一個是AnimatorListener,一個AnimatorUpdateListener,通過程式碼我們檢視它們的區別。 AnimatorListener主要是用來監聽動畫不同狀態的監聽器,從程式碼中我們可以看到它有四種不同的狀態,當我們需要在不同狀態中進行不同操作時,我們可以實現這個監聽器。AnimatorUpdateListener是監聽ValueAnimaitor的值不斷變化的過程,通常使用這個監聽器更新控制元件狀態,實現動畫過程。

// AnimatorListener主要是用來監聽動畫不同狀態的監聽器
animator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        Log.i("TAG", "start");
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        Log.i("TAG", "end");
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        Log.i("TAG", "cancel");
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        Log.i("TAG", "repeat");
    }
});複製程式碼
// AnimatorUpdateListener是監聽ValueAnimaitor的值不斷變化的過程
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.i("TAG", "curVal:" + animation.getAnimatedValue());
    }
});複製程式碼

三、Interpolator

Interpolator,譯名插值器,在我的意思裡就是加速器,即這是一個改變我們動畫速率的一個工具,可以實現加速、減速、勻速等這些特效。我們在Android基礎夯實--重溫動畫(一)之Tween Animation第五部分講了Android提供給我們使用的插值器,其實屬性動畫和檢視動畫是共用一套Interpolator的。在上面我們講到,在屬性動畫中,我們可以通過setInterpolator (TimeInterpolator value)來給我們的動畫增加一個插值器,傳入引數是TimeInterpolator,通過查閱API,我們可以知道,TimeInterpolator是一個介面。我們再來看看它和我們常用的插值器的關係。

我們常用的插值器,如AccelerateDecelerateInterpolator,AccelerateInterpolator, AnticipateInterpolator,AnticipateOvershootInterpolator等,它們的父類是BaseInterpolator。

而BaseInterpolator是實現了Interpolator,而Interpolator則是繼承TimeInterpolator介面。所以究其根源,我們常用的插值器和屬性動畫使用的TimeInterpolator其實是同一個東西。

既然瞭解了它們是同一個東西,那麼我們就需要了解怎麼來實現一個自己的Interpolator了,一般我們只要繼承BaseInterpolator,並實現它的getInterpolation(float input)方法就行了。

舉個例子,Android提供給我們的LinearInterpolator(這是一個勻速插值器)中,它的getInterpolation是這樣的:

public float getInterpolation(float input) {
    return input;
}複製程式碼

首先我們看一下引數input是什麼,input表示當前動畫的進度,它的取值範圍是0-1,0代表起點,1代表終點,隨著動畫的播放,input從0到1逐漸變大;而返回值就是指當前的實際進度,聽起來有點拗口,我們可以這麼想,例如本來當input為0.1的時候,我們返回值如果大於0.1,那麼就說明我們從0到0.1這個階段是一個加速階段,如果小於0.1,就說明這是一個減速過程。可以看到LinearInterpolator是直接把input返回,可以知道這是一個勻速的過程。

再來看看AccelerateDecelerateInterpolator,這是開始和結束速度慢,中間部分加速。我們來看一下它的getInterpolation函式:

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}複製程式碼

可以看到這就是一個餘弦公式,因為0-1這個時間內,剛開始和結束前這兩個部分斜率是比較低的,所以速度會比較慢,但是中間部分斜率明顯變大,所以中間部分呈現加速狀態。

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
餘弦公式

經過這兩個例子,我們大概知道,當我們需要實現一個Interpolator時,只需要繼承BaseInterpolator,並實現它的getInterpolation(float input)方法就行了,舉個例子:實現一個0-0.25秒內到達3/4,0.25-0.75秒內從3/4退回1/4,最後0.25秒內從1/4達到終點,先上效果圖讓大家比較直觀瞭解:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
Demo

所以我們可以很清楚的列出關係式:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
關係式

那麼在getInterpolation中,對應根據input列出演算法:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
演算法

那麼程式碼也自然出來了:

class MyInterpolator extends BaseInterpolator {
    @Override
    public float getInterpolation(float input) {
        if (input <= 0.25) {
            return 3 * input;
        } else if (input <= 0.75) {
            return (1 - input);
        } else {
            return 3 * input - 2;
        }
    }
}複製程式碼

四、Evaluator

Evaluator在屬性動畫中也是起著重要的一環。先看一張圖:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
Evaluator

我們可以看到,當Interpolator返回了當前進度滯後,Evaluator就會根據進度來計算出確定的值,以供監聽器返回,所以我們就可以知道了,Evaluator其實就是一個根據我們需求而製作的一個計算器。

其實在上面的例子我已經簡單地教大家自定義了一個Evaluator,在屬性動畫中,Android 也為我們提供了很多的Evaluator,例如IntEvaluator,FloatEvaluator等,我們可以先看一下IntEvaluator的底層實現:

public class IntEvaluator implements TypeEvaluator<Integer> {

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}複製程式碼

程式碼非常的簡單,只是重寫了一個evaluate方法,在返回值中是一條公式,就是根據開始值和結束值,當前進度,計算結果,並返回,這條公式也是非常簡單,這裡就不詳說了。但是實際開發中,有時候原生的Evaluator不適合我們使用的時候,我們就需要自定義一個Evaluator,正如我上面的例子中用到的,當我們使用了自定義的Object作為初始值和結束值時,我們就需要定義一個自己的Evaluator。下面舉一個為了自定義而自定義的Evaluator:

Android基礎夯實--重溫動畫(四)之屬性動畫 ValueAnimator詳解
Evaluator

由圖可知,自定義的Evaluator就是在FloatEvalutor的基礎之上加了200個畫素,而我自定義的Evaluator也是修改了以下FloatEvaluator的程式碼:

class MyEvaluator implements TypeEvaluator<Float> {

    @Override
    public Float evaluate(float fraction, Float startValue, Float endValue) {
        return startValue + fraction * (endValue - startValue) + 200;
    }
}複製程式碼

這是FloatEvaluator的程式碼:

public class FloatEvaluator implements TypeEvaluator<Number> {
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}複製程式碼

所以到這裡大家也可以大概瞭解了怎麼自定義Evaluator,非常的簡單,實現TypeEvaluator介面,並傳入一個型別,也就是初始值和結束值的型別,然後重寫evaluate方法,根據當前進度fraction來計算當前的返回值即可。

五、 總結

總體來說,ValueAnimator並不會很難,只要我們掌握了Animator的初始化、初始值、結束值、fraction、Evaluator、監聽器的概念,那麼我們基本掌握了ValueAnimator的使用,當然,伴隨著我們的重複使用、加深理解,當然我們離熟悉掌握ValueAnimator也不遠了。當然Animator中除了ValueAnimator以外,還有ObjectAnimator,這也是一個非常重要的概念,下一篇,我給大家帶來ObjectAnimator的詳解。

相關文章