Android屬性動畫徹底解析(一)

                                       Android屬性動畫徹底解析(一)

1.爲何要引入屬性動畫?

android提供了幾種動畫類型:View Animation 、Drawable Animation 、Property Animation 。View Animation至關簡單,不過只能支持簡單的縮放、平移、旋轉、透明度基本的動畫,且有必定的侷限性。好比:你但願View有一個顏色的切換動畫;你但願可使用3D旋轉動畫;你但願當動畫中止時,View的位置就是當前的位置;這些View Animation都沒法作到。View Animation只能對「對View進行操做」,沒錯,補間動畫是隻可以做用在View上的。補間動畫還有一個致命的缺陷,就是它只是改變了View的顯示效果而已,而不會真正去改變View的屬性。android

2.相關API

Property Animation故名思議就是經過動畫的方式改變對象的屬性了,咱們首先須要瞭解幾個屬性:ide

Duration動畫的持續時間,默認300ms。測試

Time interpolation:時間差值,乍一看不知道是什麼,可是我說LinearInterpolator、AccelerateDecelerateInterpolator,你們必定知道是幹嗎的了,定義動畫的變化率。動畫

Repeat count and behavior:重複次數、以及重複模式;能夠定義重複多少次;重複時從頭開始,仍是反向。ui

Animator sets: 動畫集合,你能夠定義一組動畫,一塊兒執行或者順序執行。lua

Frame refresh delay:幀刷新延遲,對於你的動畫,多久刷新一次幀;默認爲10ms,但最終依賴系統的當前狀態;基本不用管。spa

相關的類設計

ObjectAnimator  動畫的執行類,後面詳細介紹日誌

ValueAnimator 動畫的執行類,後面詳細介紹 code

AnimatorSet 用於控制一組動畫的執行:線性,一塊兒,每一個動畫的前後執行等。

AnimatorInflater 用戶加載屬性動畫的xml文件

TypeEvaluator  類型估值,主要用於設置動畫操做屬性的值。

TimeInterpolator 時間插值,上面已經介紹。

總的來講,屬性動畫就是,動畫的執行類來設置動畫操做的對象的屬性、持續時間,開始和結束的屬性值,時間差值等,而後系統會根據設置的參數動態的變化對象的屬性。

3.ValueAnimator

ValueAnimator是整個屬性動畫機制當中最核心的一個類,前面咱們已經提到了,屬性動畫的運行機制是經過不斷地對值進行操做來實現的,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。它的內部使用一種時間循環的機制來計算值與值之間的動畫過渡,咱們只須要將初始值和結束值提供給ValueAnimator,而且告訴它動畫所需運行的時長,那麼ValueAnimator就會自動幫咱們完成從初始值平滑地過渡到結束值這樣的效果。除此以外,ValueAnimator還負責管理動畫的播放次數、播放模式、以及對動畫設置監聽器等,確實是一個很是重要的類。

可是ValueAnimator的用法卻一點都不復雜,咱們先從最簡單的功能看起吧,好比說想要將一個值從0平滑過渡到1,時長1000毫秒,就能夠這樣寫:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ValueAnimator animator = ValueAnimator.ofFloat(0f,1f);
        animator.setDuration(1000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float currentValue = (Float) valueAnimator.getAnimatedValue();
                Log.d("ValueAnimator","current Value is"+currentValue);
            }
        });
        animator.start();
    }

}

運行結果如圖:

從打印日誌的值咱們就能夠看出,ValueAnimator確實已經在正常工做了,值在1000毫秒的時間內從0平滑過渡到了1,而這個計算工做就是由ValueAnimator幫助咱們完成的。另外ofFloat()方法當中是能夠傳入任意多個參數的,所以咱們還能夠構建出更加複雜的動畫邏輯,好比說將一個值在6秒內從0過渡到6,再過渡到4,再過渡到11,就能夠這樣寫:

ValueAnimator animator = ValueAnimator.ofFloat(0f,6f,4f,11f);
        animator.setDuration(1000);

animator.start();

固然也許你並不須要小數位數的動畫過渡,可能你只是但願將一個整數值從0平滑地過渡到80,那麼也很簡單,只須要調用ValueAnimator的ofInt()方法就能夠了,以下所示:

ValueAnimator anim = ValueAnimator.ofInt(0, 80);

ValueAnimator當中最經常使用的應該就是ofFloat()和ofInt()這兩個方法了,另外還有一個ofObject()方法,我會在下篇文章進行講解。

那麼除此以外,咱們還能夠調用setStartDelay()方法來設置動畫延遲播放的時間,調用setRepeatCount()和setRepeatMode()方法來設置動畫循環播放的次數以及循環播放的模式,循環模式包括RESTART和REVERSE兩種,分別表示從新播放和倒序播放的意思。

3.ObjectAnimator

相比於ValueAnimator,ObjectAnimator可能纔是咱們最常接觸到的類,由於ValueAnimator只不過是對值進行了一個平滑的動畫過渡,但咱們實際使用到這種功能的場景好像並很少。而ObjectAnimator則就不一樣了,它是能夠直接對任意對象的任意屬性進行動畫操做的,好比說View的alpha屬性。

不過雖然說ObjectAnimator會更加經常使用一些,可是它實際上是繼承自ValueAnimator的,底層的動畫實現機制也是基於ValueAnimator來完成的,所以ValueAnimator仍然是整個屬性動畫當中最核心的一個類。那麼既然是繼承關係,說明ValueAnimator中可使用的方法在ObjectAnimator中也是能夠正常使用的,它們的用法也很是相似,這裏若是咱們想要將一個TextView在5秒中內從常規變換成全透明,再從全透明變換成常規,就能夠這樣寫:

/**
 * quagungle 2016/11/22
 */

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(tv_test);

    }

    public void test(View view){
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"alpha",1f,0f,1f);
        animator.setDuration(5000);
        animator.start();
    }
}

能夠看到,咱們仍是調用了ofFloat()方法來去建立一個ObjectAnimator的實例,只不過ofFloat()方法當中接收的參數有點變化了。這裏第一個參數要求傳入一個object對象,咱們想要對哪一個對象進行動畫操做就傳入什麼,這裏我傳入了一個textview。第二個參數是想要對該對象的哪一個屬性進行動畫操做,因爲咱們想要改變TextView的不透明度,所以這裏傳入"alpha"。後面的參數就是不固定長度了,想要完成什麼樣的動畫就傳入什麼值,這裏傳入的值就表示將TextView從常規變換成全透明,再從全透明變換成常規。以後調用setDuration()方法來設置動畫的時長,而後調用start()方法啓動動畫,效果以下圖所示:

學會了這一個用法以後,其它的用法咱們就能夠觸類旁通了,那好比說咱們想要將TextView進行一次360度的旋轉,就能夠這樣寫:

public void test(View view){
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"rotation",0f,360f);
        animator.setDuration(5000);
        animator.start();
    }

能夠看到,這裏咱們將第二個參數改爲了"rotation",而後將動畫的初始值和結束值分別設置成0和360。因爲是靜態的圖片,效果圖就不展現,有興趣能夠本身去寫寫。

 

那麼若是想要將TextView先向左移出屏幕,而後再移動回來,就能夠這樣寫:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"translationX",currenttranslateX,-500f,currenttranslateX);
        animator.setDuration(5000);
        animator.start();
    }

這裏咱們先是調用了TextView的getTranslationX()方法來獲取到當前TextView的translationX的位置,而後ofFloat()方法的第二個參數傳入"translationX",緊接着後面三個參數用於告訴系統TextView應該怎麼移動。

 

而後咱們還能夠TextView進行縮放操做,好比說將TextView在水平方向上放大3倍再還原,就能夠這樣寫:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"scaleX",1f,3f,1f);
        animator.setDuration(5000);
        animator.start();
    }

 

到目前爲止,ObjectAnimator的用法還算是至關簡單吧,可是我相信確定會有很多朋友如今內心都有一樣一個疑問,就是ofFloat()方法的第二個參數到底能夠傳哪些值呢?目前咱們使用過了alpha、rotation、translationX和scaleY這幾個值,分別能夠完成淡入淡出、旋轉、水平移動、垂直縮放這幾種動畫,那麼還有哪些值是可使用的呢?其實這個問題的答案很是玄乎,就是咱們能夠傳入任意的值到ofFloat()方法的第二個參數當中。任意的值?相信這很出乎你們的意料吧,但事實就是如此。由於ObjectAnimator在設計的時候就沒有針對於View來進行設計,而是針對於任意對象的,它所負責的工做就是不斷地向某個對象中的某個屬性進行賦值,而後對象根據屬性值的改變再來決定如何展示出來。

那麼好比說咱們調用下面這樣一段代碼:

ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);

其實這段代碼的意思就是ObjectAnimator會幫咱們不斷地改變textview對象中alpha屬性的值,從1f變化到0f。而後textview對象須要根據alpha屬性值的改變來不斷刷新界面的顯示,從而讓用戶能夠看出淡入淡出的動畫效果。

那麼textview對象中是否是有alpha屬性這個值呢?沒有,不只textview沒有這個屬性,連它全部的父類也是沒有這個屬性的!這就奇怪了,textview當中並無alpha這個屬性,ObjectAnimator是如何進行操做的呢?其實ObjectAnimator內部的工做機制並非直接對咱們傳入的屬性名進行操做的,而是會去尋找這個屬性名對應的get和set方法,所以alpha屬性所對應的get和set方法應該就是:

public void setAlpha(float value);  
public float getAlpha();

那麼textview對象中是否有這兩個方法呢?確實有,而且這兩個方法是由View對象提供的,也就是說不只TextView可使用這個屬性來進行淡入淡出動畫操做,任何繼承自View的對象均可以的。

既然alpha是這個樣子,相信你們必定已經明白了,前面咱們所用的全部屬性都是這個工做原理,那麼View當中必定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()這些方法,不信的話你能夠到View當中去找一下。

4.組合動畫

獨立的動畫可以實現的視覺效果畢竟是至關有限的,所以將多個動畫組合到一塊兒播放就顯得尤其重要。幸運的是,Android團隊在設計屬性動畫的時候也充分考慮到了組合動畫的功能,所以提供了一套很是豐富的API來讓咱們將多個動畫組合到一塊兒。

實現組合動畫功能主要須要藉助AnimatorSet這個類,這個類提供了一個play()方法,若是咱們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)將會返回一個AnimatorSet.Builder的實例,AnimatorSet.Builder中包括如下四個方法:

  • after(Animator anim)   將現有動畫插入到傳入的動畫以後執行
  • after(long delay)   將現有動畫延遲指定毫秒後執行
  • before(Animator anim)   將現有動畫插入到傳入的動畫以前執行
  • with(Animator anim)   將現有動畫和傳入的動畫同時執行

好的,有了這四個方法,咱們就能夠完成組合動畫的邏輯了,那麼好比說咱們想要讓TextView先從屏幕外移動進屏幕,而後開始旋轉360度,旋轉的同時進行淡入淡出操做,就能夠這樣寫:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator moveIn = ObjectAnimator.ofFloat(textView,"translationX",currenttranslateX,-500f,0f);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(textView,"rotation",0f,360f);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(textView,"alpha",1f,0f,1f);
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(rotation).with(alpha).after(moveIn);
        animSet.setDuration(5000);
        animSet.start();
    }

能夠看到,這裏咱們先是把三個動畫的對象所有建立出來,而後new出一個AnimatorSet對象以後將這三個動畫對象進行播放排序,讓旋轉和淡入淡出動畫同時進行,並把它們插入到了平移動畫的後面,最後是設置動畫時長以及啓動動畫。

5.Animator監聽器

在不少時候,咱們但願能夠監聽到動畫的各類事件,好比動畫什麼時候開始,什麼時候結束,而後在開始或者結束的時候去執行一些邏輯處理。這個功能是徹底能夠實現的,Animator類當中提供了一個addListener()方法,這個方法接收一個AnimatorListener,咱們只須要去實現這個AnimatorListener就能夠監聽動畫的各類事件了。

你們已經知道,ObjectAnimator是繼承自ValueAnimator的,而ValueAnimator又是繼承自Animator的,所以無論是ValueAnimator仍是ObjectAnimator都是可使用addListener()這個方法的。另外AnimatorSet也是繼承自Animator的,所以addListener()這個方法算是個通用的方法。

添加一個監聽器的代碼以下所示:

anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationRepeat(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationCancel(Animator animation) {  
    }  
});

能夠看到,咱們須要實現接口中的四個方法,onAnimationStart()方法會在動畫開始的時候調用,onAnimationRepeat()方法會在動畫重複執行的時候調用,onAnimationEnd()方法會在動畫結束的時候調用,onAnimationCancel()方法會在動畫被取消的時候調用。

可是也許不少時候咱們並不想要監聽那麼多個事件,可能我只想要監聽動畫結束這一個事件,那麼每次都要將四個接口所有實現一遍就顯得很是繁瑣。不要緊,爲此Android提供了一個適配器類,叫做AnimatorListenerAdapter,使用這個類就能夠解決掉實現接口繁瑣的問題了,以下所示:

anim.addListener(new AnimatorListenerAdapter() {  
});

這裏咱們向addListener()方法中傳入這個適配器對象,因爲AnimatorListenerAdapter中已經將每一個接口都實現好了,因此這裏不用實現任何一個方法也不會報錯。那麼若是我想監聽動畫結束這個事件,就只須要單獨重寫這一個方法就能夠了,以下所示:

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
});

本身在測試過程當中:不知道咋回事onAnimationStart()並無被調用。

6.使用XML編寫動畫

咱們可使用代碼來編寫全部的動畫功能,這也是最經常使用的一種作法。不過,過去的補間動畫除了使用代碼編寫以外也是可使用XML編寫的,所以屬性動畫也提供了這一功能,即經過XML來完成和代碼同樣的屬性動畫功能。

經過XML來編寫動畫可能會比經過代碼來編寫動畫要慢一些,可是在重用方面將會變得很是輕鬆,好比某個將通用的動畫編寫到XML裏面,咱們就能夠在各個界面當中輕鬆去重用它。

若是想要使用XML來編寫動畫,首先要在res目錄下面新建一個animator文件夾,全部屬性動畫的XML文件都應該存放在這個文件夾當中。而後在XML文件中咱們一共可使用以下三種標籤:

  • <animator>  對應代碼中的ValueAnimator
  • <objectAnimator>  對應代碼中的ObjectAnimator
  • <set>  對應代碼中的AnimatorSet

那麼好比說咱們想要實現一個從0到100平滑過渡的動畫,在XML當中就能夠這樣寫:

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>

而若是咱們想將一個視圖的alpha屬性從1變成0,就能夠這樣寫:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>

其實XML編寫動畫在可讀性方面仍是挺高的,上面的內容相信不用我作解釋你們也都看得懂吧。

另外,咱們也可使用XML來完成複雜的組合動畫操做,好比將一個視圖先從屏幕外移動進屏幕,而後開始旋轉360度,旋轉的同時進行淡入淡出操做,就能夠這樣寫:

<set xmlns:android="http://schemas.android.com/apk/res/android"  
    android:ordering="sequentially" >  
  
    <objectAnimator  
        android:duration="2000"  
        android:propertyName="translationX"  
        android:valueFrom="-500"  
        android:valueTo="0"  
        android:valueType="floatType" >  
    </objectAnimator>  
  
    <set android:ordering="together" >  
        <objectAnimator  
            android:duration="3000"  
            android:propertyName="rotation"  
            android:valueFrom="0"  
            android:valueTo="360"  
            android:valueType="floatType" >  
        </objectAnimator>  
  
        <set android:ordering="sequentially" >  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="1"  
                android:valueTo="0"  
                android:valueType="floatType" >  
            </objectAnimator>  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="0"  
                android:valueTo="1"  
                android:valueType="floatType" >  
            </objectAnimator>  
        </set>  
    </set>  
  
</set>

這段XML實現的效果和咱們剛纔經過代碼來實現的組合動畫的效果是如出一轍的,每一個參數的含義都很是清楚,相信你們都是一看就懂,我就再也不一一解釋了。

最後XML文件是編寫好了,那麼咱們如何在代碼中把文件加載進來並將動畫啓動呢?只需調用以下代碼便可:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
animator.setTarget(view);  
animator.start();

到此屬性動畫的基本介紹已經完畢!

相關文章
相關標籤/搜索