Android圖文具體解釋屬性動畫
Android中的動畫分為視圖動畫(View Animation)、屬性動畫(Property Animation)以及Drawable動畫。從Android 3.0(API Level 11)開始。Android開始支持屬性動畫,本文主要解說怎樣使用屬性動畫。
關於視圖動畫能夠參見博文《Android四大視圖動繪圖文具體解釋》。
概述
視圖動畫局限比較大。例如以下所述:
視圖動畫僅僅能使用在View上面。
視圖動畫並沒有真正改變View相應的屬性值,這導致了UI效果與實際View狀態存在差異。並導致了一系列怪異行為,比方在使用了視圖動畫TranslateAnimation的View的UI上對其觸摸,你可能吃驚地發現並沒有觸發觸摸事件。
鑒於視圖動畫以上缺陷。從Android 3.0引入了屬性動畫。屬性動畫具有以下特性:
屬性動畫應用面更廣。不僅僅應用於View,能夠將其應用到隨意的對象上面。且該對象不須要具有UI界面。
當將屬性動畫作用於某個對象時,能夠通過調用對象的setXXX方法實際改變對象的值。所以,當將屬性動畫作用於某個View時,View對象相應的屬性值會被改變。
我們看一下屬性動畫時怎樣工作的。
事實上屬性動畫的工作原理並不復雜,假設一個對象有一個屬性x。我們想通過屬性動畫動態更改該值,假設我們想在40ms內將x的值從0漸變到40,那麽例如以下圖所看到的:
隨著時間的增長,相應的x值也相應地線性漸變。當動畫完畢時,x的值就是我們設置的終於值40。假設x值線性漸變。那麽x的變化速度就是勻速的。事實上,我們也能夠變速地改變x的值,這會我們能夠一開始加速增加x的值,後面減速增加x的值。例如以下圖所看到的:
如上圖所看到的,在前20ms。x值加速增大,在後20ms。x值增大的速度減少。
事實上。每種改變x值速度的方式都叫做時間插值器TimeInterpolator,第一張圖中使用的時間插值器叫做LinearInterpolator,第二張圖中使用的時間插值器叫做AccelerateDecelerateInterpolator。動畫開始後,時間插值器會依據相應的算法計算出某一時刻x的值。然後我們就能夠用該計算出的值更新對象中x屬性的值,這就是屬性動畫的基本工作原理。
屬性動畫中基本的類例如以下圖所看到的:
以下會對上述類分別進行解說。
Animator
屬性動畫基本的類都在android.animation命名空間下,Animator是屬性動畫的基類。其是一個抽象類。該類定義了很多重要的方法,例如以下所看到的:
setDuration(long duration)
通過setDuration方法能夠設置動畫總共的持續時間。以毫秒為單位。start()
通過start方法能夠啟動動畫。動畫啟動後不一定會馬上運行。假設之前通過調用setStartDelay方法設置了動畫延遲時間,那麽會在經過延遲時間之後再運行動畫;假設沒有設置過動畫的延遲時間,那麽動畫在調用了start()方法之後會馬上運行。在調用了start()方法之後。動畫的isStarted()方法就會返回true;在動畫真正運行起來之後,動畫的isRunning()方法就會返回true,這時候動畫才會調用TimeInterpolator才開始工作計算屬性在某個時刻的值。調用動畫的start()方法所在的線程必須綁定了一個Looper對象,假設沒有綁定就會報錯。
當然,UI線程(即主線程)早就默認綁定了一個Looper對象,所以在主線程中我們就無需操心這個問題。假設我們想在一個View上使用屬性動畫。那麽我們應該保證我們是在UI線程上調用的動畫的start()方法。start()方法運行後會觸發動畫監聽器AnimatorListener的onAnimationStart方法的運行。
setStartDelay(long startDelay)
能夠通過調用setStartDelay方法設置動畫的延遲運行時間,比方調用setStartDelay(1000)意味著動畫在運行了start()方法1秒之後才真正運行。這樣的情況下,在調用了start()方法之後,isStarted()方法返回true,表示動畫已經啟動了,可是在start()方法調用後1s內,isRunning()方法返回false。表示動畫還未真正運行。比方在start()方法調用後第0.5秒,因為動畫還在延遲階段,所以isRunning()返回false;在start()方法運行1秒之後。isStarted()方法還是返回true。isRunning()方法也返回了true。表示動畫已經真正開始運行了。通過調用getStartDelay()方法能夠返回我們設置的動畫延遲啟動時間,默認值是0。setInterpolator(TimeInterpolator value)
我們能夠通過調用setInterpolator方法改變動畫所使用的時間插值器,因為視圖動畫也須要使用時間插值器,所以我們能夠使用android.view.animation命名空間下的一系列插值器,將其與屬性動畫一起工作。通過動畫的getInterpolator方法能夠獲取我們設置的時間插值器。setTarget(Object target)
能夠通過調用動畫的setTarget方法設置其要操作的對象,這樣能夠更新該對象的某個屬性值。實際上,該方法對於ValueAnimator作用不大,因為ValueAnimator不是直接與某個對象打交道的。setTarget方法對於ObjectAnimator作用較大,因為ObjectAnimator須要綁定某個要操作的對象,以下會具體介紹。
pause()
Android中API Level 19在Animator中增加了pause()方法,該方法能夠暫停動畫的運行。調用pause()方法的線程必須與調用start()方法的線程是同一個線程。假設動畫還沒有運行start()或者動畫已經結束了,那麽調用pause()方法沒有不論什麽影響,直接被忽略。
當運行了pause()方法後,動畫的isPaused()方法會返回true。
pause()方法運行後會觸發動畫監聽器AnimatorPauseListener的onAnimationPause方法的運行。
resume()
假設動畫通過調用pause()方法暫停了。那麽之後能夠通過調用resume()方法讓動畫從上次暫停的地方繼續運行。resume()方法也是從API Level 19引入的,而且調用resume()方法的線程必須與調用start()方法的線程是同一個線程。假設動畫沒有處於暫停狀態(即isPaused()返回false)。那麽調用resume()方法會被忽略。resume()方法運行後會觸發動畫監聽器AnimatorPauseListener的onAnimationResume方法的運行。
end
end()方法運行後。動畫會結束運行。直接從當前狀態跳轉到終於的完畢狀態,並將屬性值分配成動畫的終止值,並觸發動畫監聽器AnimatorListener的onAnimationEnd方法的運行。cancel()
cancel()方法運行後,動畫也會結束運行,可是與調用end方法不同的是,其不會將屬性值分配給動畫的終止值。比方一個動畫在400ms內將對象的x屬性從0漸變為40。當運行到第200ms時調用了cancel()方法。那麽屬性x的終於值是20,而不是40,這是與調用end()方法不同的,假設在第200ms調用了end()方法,那麽屬性x的終於值是40。
調用cancel()方法後。會先觸發AnimatorListener的onAnimationCancel方法的運行,然後觸發onAnimationEnd方法的運行。
clone()
Animator實現了java.lang.Cloneable接口。Animator的clone()方法會對動畫進行拷貝,可是該方法默認實現的僅僅是淺拷貝,子類能夠重寫該方法以實現深拷貝。addListener (Animator.AnimatorListener listener)
能夠通過addListener方法向Animator增加動畫監聽器,該方法接收的是AnimatorListener接口類型的參數,其具有四個方法:onAnimationStart、onAnimationCancel、onAnimationEnd、onAnimationRepeat。我們上面已經介紹了前三個方法,onAnimationRepeat方法會在動畫在反復播放的時候被回調。Android中的AnimatorListenerAdapter類是個抽象類。事實上現了AnimatorListener接口。並為全部方法提供了一個空實現。addPauseListener (Animator.AnimatorPauseListener listener)
能夠通過addPauseListener方法能夠向Animator增加動畫暫停相關的監聽器。該方法接收的是AnimatorPauseListener接口類型的參數,具有兩個方法:onAnimationPause和onAnimationResume。上面已經提到過,在此不再贅述。AnimatorListenerAdapter相同也實現了AnimatorPauseListener接口,並為全部方法提供了一個空實現。
ValueAnimator
ValueAnimator繼承自抽象類Animator。要讓屬性動畫漸變式地更改對象中某個屬性的值,可分兩步操作:第一步,動畫須要計算出某一時刻屬性值應該是多少;第二步,須要將計算出的屬性值賦值給動畫的屬性。ValueAnimator僅僅實現了第一步,也就是說ValueAnimator僅僅負責以動畫的形式不斷計算不同一時候刻的屬性值,但須要我們開發人員自己寫代碼將計算出的值通過對象的setXXX等方法更新對象的屬性值。
ValueAnimator中有兩個比較重要的屬性。一個是TimeInterpolator類型的屬性,還有一個是TypeEvaluator類型的屬性。TimeInterpolator指的就是時間插值器。在上面我們已經介紹過,在此不再贅述。
TypeEvaluator是什麽呢?TypeEvaluator表示的是ValueAnimator對哪種類型的值進行動畫處理。
ValueAnimator提供了四個靜態方法ofFloat()、ofInt()、ofArgb()和ofObject(),通過這四個方法能夠對不同種類型的值進行動畫處理。這四個方法相應了四種TypeEvaluator。以下會具體說明。
public static ValueAnimator ofFloat (float… values)
ofFloat方法接收一系列的float類型的值。其內部使用了FloatEvaluator。通過該方法ValueAnimator能夠對float值進行動畫漸變。其用法例如以下所看到的:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 500f); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float deltaY = (float)animation.getAnimatedValue(); textView.setTranslationY(deltaY); } }); //默認duration是300毫秒 valueAnimator.setDuration(3000); valueAnimator.start();
其效果例如以下所看到的:
我們通過構造函數指定了動畫的起始值為0,終止值為500。動畫的默認持續時間是300毫秒,我們通過setDuration()方法設置為3000毫秒。
該動畫會在3秒內。將值從0到500動畫漸變。
ValueAnimator提供了一個addUpdateListener方法,能夠通過該方法向其增加AnimatorUpdateListener類型的監聽器。
AnimatorUpdateListener有一個onAnimationUpdate方法。ValueAnimator會每隔一定時間(默認間隔10ms)計算屬性的值,每當計算的時候就會回調onAnimationUpdate方法。在該方法中,我們通過調用ValueAnimator的getAnimatedValue()方法獲取到當前動畫計算出的屬性值,然後我們將該值傳入textView的setTranslationY()方法中,從而更新了textView的位置,這樣就通過ValueAnimator以動畫的形式移動textView。
public static ValueAnimator ofInt (int… values)
ofInt方法與ofFloat方法非常相似,僅僅只是ofInt方法接收int類型的值,ofInt方法內部使用了IntEvaluator,其具體使用可參考上面ofFloat的使用代碼。在此不再贅述。public static ValueAnimator ofArgb (int… values)
從API Level 21開始,ValueAnimator中增加了ofArgb方法,該方法接收一些列代表了顏色的int值。其內部使用了ArgbEvaluator。能夠用該方法實現將一個顏色動畫漸變到還有一個顏色,我們從中能夠不斷獲取中間動畫產生的顏色值。你可能納悶,既然傳入的還是int值,那直接用ofInt方法不即可了嗎,幹嘛還要新增一個ofArgb方法呢?實際上用ofInt方法是不能完畢顏色動畫漸變的。我們知道一個int值包括四個字節,在Android中第一個字節代表Alpha分量,第二個字節代表Red分量。第三個字節代表Green分量,第四個字節代表Blue分量,且我們經常使用16進制表示顏色。這樣看起來更明顯易懂一些,比方int值0xffff0000表示的紅色,0xff00ff00表示的是綠色,最前面的ff表示的是Alpha。ofArgb方法會通過ArgbEvaluator將顏色拆分成四個分量,然後分別對各個分量進行動畫計算。然後將四個計算完的分量再又一次組合成一個表示顏色的int值。這就是ofArgb方法的工作原理。用法例如以下所看到的:
//ValueAnimator.ofArgb()方法是在API Level 21中才增加的 if(Build.VERSION.SDK_INT >= 21){ //起始顏色為紅色 int startColor = 0xffff0000; //終止顏色為綠色 int endColor = 0xff00ff00; ValueAnimator valueAnimator = ValueAnimator.ofArgb(startColor, endColor); valueAnimator.setDuration(3000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int color = (int)animation.getAnimatedValue(); textView.setBackgroundColor(color); } }); valueAnimator.start(); }
效果例如以下所看到的:
我們將TextView的顏色通過動畫從紅色漸變到綠色。
public static ValueAnimator ofObject (TypeEvaluator evaluator, Object… values)
因為我們要進行動畫處理的值是各種各樣的。可能不是float、int或顏色值,那我們怎麽使用屬性動畫呢?為此,ValueAnimator提供了一個ofObject方法,該方法接收一個TypeEvaluator類型的參數,我們須要實現該接口TypeEvaluator的evaluate方法,僅僅要我們實現了TypeEvaluator接口,我們就能通過ofObject方法處理隨意類型的數據。我們之前提到ofArgb方法是從API Level 21才引入的。假設我們想在之前的這之前的版本號中使用ofArgb的功能,怎麽辦呢?我們能夠擴展TypeEvaluator,從而通過ofObject方法實現ofArgb方法的邏輯,例如以下所看到的:
//起始顏色為紅色 int startColor = 0xffff0000; //終止顏色為綠色 int endColor = 0xff00ff00; ValueAnimator valueAnimator = ValueAnimator.ofObject(new TypeEvaluator() { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { //從初始的int類型的顏色值中解析出Alpha、Red、Green、Blue四個分量 int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; //從終止的int類型的顏色值中解析出Alpha、Red、Green、Blue四個分量 int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; //分別對Alpha、Red、Green、Blue四個分量進行計算, //終於合成一個完整的int型的顏色值 return (int)((startA + (int)(fraction * (endA - startA))) << 24) | (int)((startR + (int)(fraction * (endR - startR))) << 16) | (int)((startG + (int)(fraction * (endG - startG))) << 8) | (int)((startB + (int)(fraction * (endB - startB)))); } }, startColor, endColor); valueAnimator.setDuration(3000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int color = (int)animation.getAnimatedValue(); textView.setBackgroundColor(color); } }); valueAnimator.start();
以上代碼實現的效果與ofArgb實現的效果是一樣的。都是將TextView從紅色漸變到綠色,就不再附圖了。可是我們能夠在API Level 11及以後的版本號中都能夠使用以上ofObject的代碼,通用性更強。
ObjectAnimator
ObjectAnimator繼承自ValueAnimator。我們之前提到,要讓屬性動畫漸變式地更改對象中某個屬性的值,可分兩步操作:第一步。動畫須要計算出某一時刻屬性值應該是多少;第二步,須要將計算出的屬性值賦值給動畫的屬性。
ValueAnimator僅僅實現了第一步,也就是說ValueAnimator僅僅負責以動畫的形式不斷計算不同一時候刻的屬性值,但須要我們開發人員自己寫代碼在動畫監聽器AnimatorUpdateListener的onAnimationUpdate方法中將計算出的值通過對象的setXXX等方法更新對象的屬性值。ObjectAnimator比ValueAnimator更進一步,其會自己主動調用對象的setXXX方法更新對象中的屬性值。
ObjectAnimator重載了ofFloat()、ofInt()、ofArgb()和ofObject()等靜態方法,我們以下依次說明。
ofFloat(Object target, String propertyName, float… values)
用法例如以下所看到的:float value1 = 0f; float value2 = 500f; final ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView, "translationY", value1, value2); objectAnimator.setDuration(3000); objectAnimator.start();
以上代碼實現的效果與通過ValueAnimator的ofFloat方法實現的效果相同,此處不再附圖,可是能夠看出使用ObjectAnimator代碼更簡潔。在構造函數中。我們將textView作為target傳遞給ObjectAnimator。然後指定textView要變化的屬性是translationY,最後指定漸變範圍是從0到500。當動畫開始時,ObjectAnimator就會不斷調用textView的setTranslationY方法以更新其值。我們此處演示的是ObjectAnimator與View一起工作,事實上ObjectAnimator能夠與隨意的Object對象工作。假設要更新某個對象中名為propery的屬性,那麽該Object對象必須具有一個setProperty的setter方法能夠讓ObjectAnimator調用。
在ofFloat方法最後假設僅僅填入了一個float值,那麽ObjectAnimator須要調用對象的getXXX方法獲取對象初始的屬性值,然後從該初始的屬性值漸變到終止值。
ofInt(Object target, String propertyName, int… values)
參見ofFloat的用法。ofArgb(Object target, String propertyName, int… values)
使用代碼例如以下所看到的://ObjectAnimator.ofArgb()方法是在API Level 21中才增加的 if(Build.VERSION.SDK_INT >= 21){ int startColor = 0xffff0000; int endColor = 0xff00ff00; ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(textView, "backgroundColor", startColor, endColor); objectAnimator.setDuration(3000); objectAnimator.start(); }
效果圖參見ValueAnimator中相應的圖片。
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object… values)
使用代碼例如以下所看到的:int startColor = 0xffff0000; int endColor = 0xff00ff00; ObjectAnimator objectAnimator = ObjectAnimator.ofObject(textView, "backgroundColor", new TypeEvaluator() { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int)((startA + (int)(fraction * (endA - startA))) << 24) | (int)((startR + (int)(fraction * (endR - startR))) << 16) | (int)((startG + (int)(fraction * (endG - startG))) << 8) | (int)((startB + (int)(fraction * (endB - startB)))); } }, startColor, endColor); objectAnimator.setDuration(3000); objectAnimator.start();
效果圖參見ValueAnimator中相應的圖片。
AnimatorSet
AnimatorSet繼承自Animator。AnimatorSet表示的是動畫的集合,我們能夠通過AnimatorSet把多個動畫集合在一起,讓其串行或並行運行,從而創造出復雜的動畫效果。
我們想讓TextView先進行旋轉,然後進行平移,最後進行伸縮,我們能夠通過AnimatorSet實現該效果,代碼例如以下所看到的:
```
//anim1實現TextView的旋轉動畫
Animator anim1 = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f);
anim1.setDuration(2000);
//anim2和anim3TextView的平移動畫
Animator anim2 = ObjectAnimator.ofFloat(textView, "translationX", 0f, 300f);
anim2.setDuration(3000);
Animator anim3 = ObjectAnimator.ofFloat(textView, "translationY", 0f, 400f);
anim3.setDuration(3000);
//anim4實現TextView的伸縮動畫
Animator anim4 = ObjectAnimator.ofFloat(textView, "scaleX", 1f, 0.5f);
anim4.setDuration(2000);
//第一種方式
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(anim1, anim2, anim4);
animatorSet.playTogether(anim2, anim3);
animatorSet.start();
//另外一種方式
/*AnimatorSet anim23 = new AnimatorSet();
anim23.playTogether(anim2, anim3);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(anim1, anim23, anim4);
animatorSet.start();*/
//第三種方式
/*AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(anim1).before(anim2);
animatorSet.play(anim2).with(anim3);
animatorSet.play(anim4).after(anim2);
animatorSet.start();*/
```
效果例如以下所看到的:
動畫anim1用於旋轉TextView。anim2用於在X軸方向偏移TextView,anim3用於在Y軸方向偏移TextView,anim4用於縮放TextView。
我們在以上代碼中提供了三種方式通過AnimationSet把這四個動畫組合到一起,另外一種方式和第三種方式被凝視起來了。
事實上有非常多種辦法實現上述效果。這裏僅僅介紹一下上述三種方式的思路。
在第一種方式中。調用了animatorSet.playSequentially(anim1, anim2, anim4),該方法將anim1、anim2以及anim4按順序串聯起來放到了animatorSet中,這樣首先會讓動畫anim1運行。anim1運行完畢後,會依次運行動畫anim2,運行完anim2之後會運行動畫anim3。通過調用animatorSet.playTogether(anim2, anim3)。保證了anim2和anim3同一時候運行,即動畫anim1完畢之後會同一時候運行anim2和anim3。
在另外一種方式中,我們首先創建了一個AnimatorSet變量anim23。然後通過anim23.playTogether(anim2, anim3)將anim2和anim3組合成一個小的動畫集合。
然後我們再把anim1、anim23以及anim4一起傳入到animatorSet.playSequentially(anim1, anim23, anim4)中。這樣anim1、anim23、anim4會依次運行,而anim23中的anim2和anim3會同一時候運行。該方式同一時候也演示了能夠將一個AnimatorSet作為動畫的一部分放入還有一個AnimatorSet中。
在第三種方式中,我們使用了AnimatorSet的play方法,該方法返回AnimatorSet.Builder類型,animatorSet.play(anim1).before(anim2)確保了anim1運行完了之後運行anim2,animatorSet.play(anim2).with(anim3)確保了anim2和anim3同一時候運行,animatorSet.play(anim4).after(anim2)確保了anim2運行完了之後運行anim4。
須要說明的是animatorSet.play(anim1).before(anim2)與animatorSet.play(anim2).after(anim1)是全然等價的,之所以在上面代碼中有的寫before,有的寫after,僅僅是為了讓大家多了解一下API。
希望本文對大家學習屬性動畫有所幫助。
Android圖文具體解釋屬性動畫