圖文詳解Android屬性動(dòng)畫(huà)
Android中的動(dòng)畫(huà)分為視圖動(dòng)畫(huà)(View Animation)、屬性動(dòng)畫(huà)(Property Animation)以及Drawable動(dòng)畫(huà)。從Android 3.0(API Level 11)開(kāi)始,Android開(kāi)始支持屬性動(dòng)畫(huà),本文主要講解如何使用屬性動(dòng)畫(huà)。關(guān)于視圖動(dòng)畫(huà)可以參見(jiàn)博文《Android四大視圖動(dòng)畫(huà)圖文詳解》。
一、概述
視圖動(dòng)畫(huà)局限比較大,如下所述:
1、視圖動(dòng)畫(huà)只能使用在View上面。
2、視圖動(dòng)畫(huà)并沒(méi)有真正改變View相應(yīng)的屬性值,這導(dǎo)致了UI效果與實(shí)際View狀態(tài)存在差異,并導(dǎo)致了一系列怪異行為,比如在使用了視圖動(dòng)畫(huà)TranslateAnimation的View的UI上對(duì)其觸摸,你可能驚訝地發(fā)現(xiàn)并沒(méi)有觸發(fā)觸摸事件。
鑒于視圖動(dòng)畫(huà)以上缺陷,從Android 3.0引入了屬性動(dòng)畫(huà)。屬性動(dòng)畫(huà)具有以下特性:
1、屬性動(dòng)畫(huà)應(yīng)用面更廣,不僅僅應(yīng)用于View,可以將其應(yīng)用到任意的對(duì)象上面,且該對(duì)象不需要具有UI界面。
2、當(dāng)將屬性動(dòng)畫(huà)作用于某個(gè)對(duì)象時(shí),可以通過(guò)調(diào)用對(duì)象的setXXX方法實(shí)際改變對(duì)象的值。所以,當(dāng)將屬性動(dòng)畫(huà)作用于某個(gè)View時(shí),View對(duì)象對(duì)應(yīng)的屬性值會(huì)被改變。
我們看一下屬性動(dòng)畫(huà)時(shí)如何工作的。
其實(shí)屬性動(dòng)畫(huà)的工作原理并不復(fù)雜,假設(shè)一個(gè)對(duì)象有一個(gè)屬性x,我們想通過(guò)屬性動(dòng)畫(huà)動(dòng)態(tài)更改該值,假設(shè)我們想在40ms內(nèi)將x的值從0漸變到40,那么如下圖所示:
隨著時(shí)間的增長(zhǎng),對(duì)應(yīng)的x值也相應(yīng)地線性漸變,當(dāng)動(dòng)畫(huà)完成時(shí),x的值就是我們?cè)O(shè)置的最終值40。如果x值線性漸變,那么x的變化速度就是勻速的。其實(shí),我們也可以變速地改變x的值,這會(huì)我們可以一開(kāi)始加速增加x的值,后面減速增加x的值,如下圖所示:
如上圖所示,在前20ms,x值加速增大,在后20ms,x值增大的速度降低。
其實(shí),每種改變x值速度的方式都叫做時(shí)間插值器TimeInterpolator,第一張圖中使用的時(shí)間插值器叫做LinearInterpolator,第二張圖中使用的時(shí)間插值器叫做AccelerateDecelerateInterpolator。動(dòng)畫(huà)開(kāi)始后,時(shí)間插值器會(huì)根據(jù)對(duì)應(yīng)的算法計(jì)算出某一時(shí)刻x的值,然后我們就可以用該計(jì)算出的值更新對(duì)象中x屬性的值,這就是屬性動(dòng)畫(huà)的基本工作原理。
屬性動(dòng)畫(huà)中主要的類如下圖所示:
下面會(huì)對(duì)上述類分別進(jìn)行講解。
Animator
屬性動(dòng)畫(huà)主要的類都在android.animation命名空間下,Animator是屬性動(dòng)畫(huà)的基類,其是一個(gè)抽象類,該類定義了許多重要的方法,如下所示:
- setDuration(long duration)
通過(guò)setDuration方法可以設(shè)置動(dòng)畫(huà)總共的持續(xù)時(shí)間,以毫秒為單位。
- start()
通過(guò)start方法可以啟動(dòng)動(dòng)畫(huà),動(dòng)畫(huà)啟動(dòng)后不一定會(huì)立即運(yùn)行。如果之前通過(guò)調(diào)用setStartDelay方法設(shè)置了動(dòng)畫(huà)延遲時(shí)間,那么會(huì)在經(jīng)過(guò)延遲時(shí)間之后再運(yùn)行動(dòng)畫(huà);如果沒(méi)有設(shè)置過(guò)動(dòng)畫(huà)的延遲時(shí)間,那么動(dòng)畫(huà)在調(diào)用了start()方法之后會(huì)立即運(yùn)行。在調(diào)用了start()方法之后,動(dòng)畫(huà)的isStarted()方法就會(huì)返回true;在動(dòng)畫(huà)真正運(yùn)行起來(lái)之后,動(dòng)畫(huà)的isRunning()方法就會(huì)返回true,這時(shí)候動(dòng)畫(huà)才會(huì)調(diào)用TimeInterpolator才開(kāi)始工作計(jì)算屬性在某個(gè)時(shí)刻的值。調(diào)用動(dòng)畫(huà)的start()方法所在的線程必須綁定了一個(gè)Looper對(duì)象,如果沒(méi)有綁定就會(huì)報(bào)錯(cuò)。當(dāng)然,UI線程(即主線程)早就默認(rèn)綁定了一個(gè)Looper對(duì)象,所以在主線程中我們就無(wú)需擔(dān)心這個(gè)問(wèn)題。如果我們想在一個(gè)View上使用屬性動(dòng)畫(huà),那么我們應(yīng)該保證我們是在UI線程上調(diào)用的動(dòng)畫(huà)的start()方法。start()方法運(yùn)行后會(huì)觸發(fā)動(dòng)畫(huà)監(jiān)聽(tīng)器AnimatorListener的onAnimationStart方法的執(zhí)行。
- setStartDelay(long startDelay)
可以通過(guò)調(diào)用setStartDelay方法設(shè)置動(dòng)畫(huà)的延遲運(yùn)行時(shí)間,比如調(diào)用setStartDelay(1000)意味著動(dòng)畫(huà)在執(zhí)行了start()方法1秒之后才真正運(yùn)行,這種情況下,在調(diào)用了start()方法0.5秒之后,isStarted()方法返回true,表示動(dòng)畫(huà)已經(jīng)啟動(dòng)了,但是isRunning()方法返回false,表示動(dòng)畫(huà)還未真正運(yùn)行;在start()方法執(zhí)行1秒之后,isStarted()方法還是返回true,isRunning()方法也返回了true,表示動(dòng)畫(huà)已經(jīng)真正開(kāi)始運(yùn)行了。通過(guò)調(diào)用getStartDelay()方法可以返回我們?cè)O(shè)置的動(dòng)畫(huà)延遲啟動(dòng)時(shí)間,默認(rèn)值是0。
- setInterpolator(TimeInterpolator value)
我們可以通過(guò)調(diào)用setInterpolator方法改變動(dòng)畫(huà)所使用的時(shí)間插值器,由于視圖動(dòng)畫(huà)也需要使用時(shí)間插值器,所以我們可以使用android.view.animation命名空間下的一系列插值器,將其與屬性動(dòng)畫(huà)一起工作。通過(guò)動(dòng)畫(huà)的getInterpolator方法可以獲取我們?cè)O(shè)置的時(shí)間插值器。
- setTarget(Object target)
可以通過(guò)調(diào)用動(dòng)畫(huà)的setTarget方法設(shè)置其要操作的對(duì)象,這樣可以更新該對(duì)象的某個(gè)屬性值。實(shí)際上,該方法對(duì)于ValueAnimator作用不大,因?yàn)閂alueAnimator不是直接與某個(gè)對(duì)象打交道的。setTarget方法對(duì)于ObjectAnimator作用較大,因?yàn)镺bjectAnimator需要綁定某個(gè)要操作的對(duì)象,下面會(huì)詳細(xì)介紹。
- pause()
Android中API Level 19在Animator中加入了pause()方法,該方法可以暫停動(dòng)畫(huà)的執(zhí)行。調(diào)用pause()方法的線程必須與調(diào)用start()方法的線程是同一個(gè)線程。如果動(dòng)畫(huà)還沒(méi)有執(zhí)行start()或者動(dòng)畫(huà)已經(jīng)結(jié)束了,那么調(diào)用pause()方法沒(méi)有任何影響,直接被忽略。當(dāng)執(zhí)行了pause()方法后,動(dòng)畫(huà)的isPaused()方法會(huì)返回true。pause()方法運(yùn)行后會(huì)觸發(fā)動(dòng)畫(huà)監(jiān)聽(tīng)器AnimatorPauseListener的onAnimationPause方法的執(zhí)行。
- resume()
如果動(dòng)畫(huà)通過(guò)調(diào)用pause()方法暫停了,那么之后可以通過(guò)調(diào)用resume()方法讓動(dòng)畫(huà)從上次暫停的地方繼續(xù)運(yùn)行。resume()方法也是從API Level 19引入的,并且調(diào)用resume()方法的線程必須與調(diào)用start()方法的線程是同一個(gè)線程。如果動(dòng)畫(huà)沒(méi)有處于暫停狀態(tài)(即isPaused()返回false),那么調(diào)用resume()方法會(huì)被忽略。resume()方法運(yùn)行后會(huì)觸發(fā)動(dòng)畫(huà)監(jiān)聽(tīng)器AnimatorPauseListener的onAnimationResume方法的執(zhí)行。
- end
end()方法執(zhí)行后,動(dòng)畫(huà)會(huì)結(jié)束運(yùn)行,直接從當(dāng)前狀態(tài)跳轉(zhuǎn)到最終的完成狀態(tài),并將屬性值分配成動(dòng)畫(huà)的終止值,并觸發(fā)動(dòng)畫(huà)監(jiān)聽(tīng)器AnimatorListener的onAnimationEnd方法的執(zhí)行。
- cancel()
cancel()方法執(zhí)行后,動(dòng)畫(huà)也會(huì)結(jié)束運(yùn)行,但是與調(diào)用end方法不同的是,其不會(huì)將屬性值分配給動(dòng)畫(huà)的終止值。比如一個(gè)動(dòng)畫(huà)在400ms內(nèi)將對(duì)象的x屬性從0漸變?yōu)?0,當(dāng)運(yùn)行到第200ms時(shí)調(diào)用了cancel()方法,那么屬性x的最終值是20,而不是40,這是與調(diào)用end()方法不同的,如果在第200ms調(diào)用了end()方法,那么屬性x的最終值是40。調(diào)用cancel()方法后,會(huì)先觸發(fā)AnimatorListener的onAnimationCancel方法的執(zhí)行,然后觸發(fā)onAnimationEnd方法的執(zhí)行。
- clone()
Animator實(shí)現(xiàn)了java.lang.Cloneable接口。Animator的clone()方法會(huì)對(duì)動(dòng)畫(huà)進(jìn)行拷貝,但是該方法默認(rèn)實(shí)現(xiàn)的只是淺拷貝,子類可以重寫(xiě)該方法以實(shí)現(xiàn)深拷貝。
- addListener (Animator.AnimatorListener listener)
可以通過(guò)addListener方法向Animator添加動(dòng)畫(huà)監(jiān)聽(tīng)器,該方法接收的是AnimatorListener接口類型的參數(shù),其具有四個(gè)方法:onAnimationStart、onAnimationCancel、onAnimationEnd、onAnimationRepeat。我們上面已經(jīng)介紹了前三個(gè)方法,onAnimationRepeat方法會(huì)在動(dòng)畫(huà)在重復(fù)播放的時(shí)候被回調(diào)。Android中的AnimatorListenerAdapter類是個(gè)抽象類,其實(shí)現(xiàn)了AnimatorListener接口,并為所有方法提供了一個(gè)空實(shí)現(xiàn)。
- addPauseListener (Animator.AnimatorPauseListener listener)
可以通過(guò)addPauseListener方法可以向Animator添加動(dòng)畫(huà)暫停相關(guān)的監(jiān)聽(tīng)器,該方法接收的是AnimatorPauseListener接口類型的參數(shù),具有兩個(gè)方法:onAnimationPause和onAnimationResume,上面已經(jīng)提到過(guò),在此不再贅述。AnimatorListenerAdapter同樣也實(shí)現(xiàn)了AnimatorPauseListener接口,并為所有方法提供了一個(gè)空實(shí)現(xiàn)。
ValueAnimator
ValueAnimator繼承自抽象類Animator。要讓屬性動(dòng)畫(huà)漸變式地更改對(duì)象中某個(gè)屬性的值,可分兩步操作:第一步,動(dòng)畫(huà)需要計(jì)算出某一時(shí)刻屬性值應(yīng)該是多少;第二步,需要將計(jì)算出的屬性值賦值給動(dòng)畫(huà)的屬性。ValueAnimator只實(shí)現(xiàn)了第一步,也就是說(shuō)ValueAnimator只負(fù)責(zé)以動(dòng)畫(huà)的形式不斷計(jì)算不同時(shí)刻的屬性值,但需要我們開(kāi)發(fā)者自己寫(xiě)代碼將計(jì)算出的值通過(guò)對(duì)象的setXXX等方法更新對(duì)象的屬性值。
ValueAnimator中有兩個(gè)比較重要的屬性,一個(gè)是TimeInterpolator類型的屬性,另一個(gè)是TypeEvaluator類型的屬性。TimeInterpolator指的就是時(shí)間插值器,在上面我們已經(jīng)介紹過(guò),在此不再贅述。TypeEvaluator是什么呢?TypeEvaluator表示的是ValueAnimator對(duì)哪種類型的值進(jìn)行動(dòng)畫(huà)處理。ValueAnimator提供了四個(gè)靜態(tài)方法ofFloat()、ofInt()、ofArgb()和ofObject(),通過(guò)這四個(gè)方法可以對(duì)不同種類型的值進(jìn)行動(dòng)畫(huà)處理,這四個(gè)方法對(duì)應(yīng)了四種TypeEvaluator,下面會(huì)詳細(xì)說(shuō)明。
- public static ValueAnimator ofFloat (float… values)
ofFloat方法接收一系列的float類型的值,其內(nèi)部使用了FloatEvaluator。通過(guò)該方法ValueAnimator可以對(duì)float值進(jìn)行動(dòng)畫(huà)漸變,其使用方法如下所示:
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); } }); //默認(rèn)duration是300毫秒 valueAnimator.setDuration(3000); valueAnimator.start();
其效果如下所示:
我們通過(guò)構(gòu)造函數(shù)指定了動(dòng)畫(huà)的起始值為0,終止值為500,動(dòng)畫(huà)的默認(rèn)持續(xù)時(shí)間是300毫秒,我們通過(guò)setDuration()方法設(shè)置為3000毫秒。該動(dòng)畫(huà)會(huì)在3秒內(nèi),將值從0到500動(dòng)畫(huà)漸變。ValueAnimator提供了一個(gè)addUpdateListener方法,可以通過(guò)該方法向其添加AnimatorUpdateListener類型的監(jiān)聽(tīng)器。AnimatorUpdateListener有一個(gè)onAnimationUpdate方法,ValueAnimator會(huì)每隔一定時(shí)間(默認(rèn)間隔10ms)計(jì)算屬性的值,每當(dāng)計(jì)算的時(shí)候就會(huì)回調(diào)onAnimationUpdate方法。在該方法中,我們通過(guò)調(diào)用ValueAnimator的getAnimatedValue()方法獲取到當(dāng)前動(dòng)畫(huà)計(jì)算出的屬性值,然后我們將該值傳入textView的setTranslationY()方法中,從而更新了textView的位置,這樣就通過(guò)ValueAnimator以動(dòng)畫(huà)的形式移動(dòng)textView。
- public static ValueAnimator ofInt (int… values)
ofInt方法與ofFloat方法很類似,只不過(guò)ofInt方法接收int類型的值,ofInt方法內(nèi)部使用了IntEvaluator,其具體使用可參考上面ofFloat的使用代碼,在此不再贅述。
- public static ValueAnimator ofArgb (int… values)
從API Level 21開(kāi)始,ValueAnimator中加入了ofArgb方法,該方法接收一些列代表了顏色的int值,其內(nèi)部使用了ArgbEvaluator,可以用該方法實(shí)現(xiàn)將一個(gè)顏色動(dòng)畫(huà)漸變到另一個(gè)顏色,我們從中可以不斷獲取中間動(dòng)畫(huà)產(chǎn)生的顏色值。你可能納悶,既然傳入的還是int值,那直接用ofInt方法不就行了嗎,干嘛還要新增一個(gè)ofArgb方法呢?實(shí)際上用ofInt方法是不能完成顏色動(dòng)畫(huà)漸變的。我們知道一個(gè)int值包含四個(gè)字節(jié),在Android中第一個(gè)字節(jié)代表Alpha分量,第二個(gè)字節(jié)代表Red分量,第三個(gè)字節(jié)代表Green分量,第四個(gè)字節(jié)代表Blue分量,且我們常用16進(jìn)制表示顏色,這樣看起來(lái)更明顯易懂一些,比如int值0xffff0000表示的紅色,0xff00ff00表示的是綠色,最前面的ff表示的是Alpha。ofArgb方法會(huì)通過(guò)ArgbEvaluator將顏色拆分成四個(gè)分量,然后分別對(duì)各個(gè)分量進(jìn)行動(dòng)畫(huà)計(jì)算,然后將四個(gè)計(jì)算完的分量再重新組合成一個(gè)表示顏色的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的顏色通過(guò)動(dòng)畫(huà)從紅色漸變到綠色。
- public static ValueAnimator ofObject (TypeEvaluator evaluator, Object… values)
由于我們要進(jìn)行動(dòng)畫(huà)處理的值是各種各樣的,可能不是float、int或顏色值,那我們?cè)趺词褂脤傩詣?dòng)畫(huà)呢?為此,ValueAnimator提供了一個(gè)ofObject方法,該方法接收一個(gè)TypeEvaluator類型的參數(shù),我們需要實(shí)現(xiàn)該接口TypeEvaluator的evaluate方法,只要我們實(shí)現(xiàn)了TypeEvaluator接口,我們就能通過(guò)ofObject方法處理任意類型的數(shù)據(jù)。我們之前提到ofArgb方法是從API Level 21才引入的,如果我們想在之前的這之前的版本中使用ofArgb的功能,怎么辦呢?我們可以擴(kuò)展TypeEvaluator,從而通過(guò)ofObject方法實(shí)現(xiàn)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四個(gè)分量 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四個(gè)分量 int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; //分別對(duì)Alpha、Red、Green、Blue四個(gè)分量進(jìn)行計(jì)算, //最終合成一個(gè)完整的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();
以上代碼實(shí)現(xiàn)的效果與ofArgb實(shí)現(xiàn)的效果是一樣的,都是將TextView從紅色漸變到綠色,就不再附圖了,但是我們可以在API Level 11及以后的版本中都可以使用以上ofObject的代碼,通用性更強(qiáng)。
ObjectAnimator
ObjectAnimator繼承自ValueAnimator。我們之前提到,要讓屬性動(dòng)畫(huà)漸變式地更改對(duì)象中某個(gè)屬性的值,可分兩步操作:第一步,動(dòng)畫(huà)需要計(jì)算出某一時(shí)刻屬性值應(yīng)該是多少;第二步,需要將計(jì)算出的屬性值賦值給動(dòng)畫(huà)的屬性。ValueAnimator只實(shí)現(xiàn)了第一步,也就是說(shuō)ValueAnimator只負(fù)責(zé)以動(dòng)畫(huà)的形式不斷計(jì)算不同時(shí)刻的屬性值,但需要我們開(kāi)發(fā)者自己寫(xiě)代碼在動(dòng)畫(huà)監(jiān)聽(tīng)器AnimatorUpdateListener的onAnimationUpdate方法中將計(jì)算出的值通過(guò)對(duì)象的setXXX等方法更新對(duì)象的屬性值。ObjectAnimator比ValueAnimator更進(jìn)一步,其會(huì)自動(dòng)調(diào)用對(duì)象的setXXX方法更新對(duì)象中的屬性值。
ObjectAnimator重載了ofFloat()、ofInt()、ofArgb()和ofObject()等靜態(tài)方法,我們下面依次說(shuō)明。
- 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();
以上代碼實(shí)現(xiàn)的效果與通過(guò)ValueAnimator的ofFloat方法實(shí)現(xiàn)的效果相同,此處不再附圖,但是可以看出使用ObjectAnimator代碼更簡(jiǎn)潔。在構(gòu)造函數(shù)中,我們將textView作為target傳遞給ObjectAnimator,然后指定textView要變化的屬性是translationY,最后指定漸變范圍是從0到500。當(dāng)動(dòng)畫(huà)開(kāi)始時(shí),ObjectAnimator就會(huì)不斷調(diào)用textView的setTranslationY方法以更新其值。我們此處演示的是ObjectAnimator與View一起工作,其實(shí)ObjectAnimator可以與任意的Object對(duì)象工作。如果要更新某個(gè)對(duì)象中名為propery的屬性,那么該Object對(duì)象必須具有一個(gè)setProperty的setter方法可以讓ObjectAnimator調(diào)用。在ofFloat方法最后如果只填入了一個(gè)float值,那么ObjectAnimator需要調(diào)用對(duì)象的getXXX方法獲取對(duì)象初始的屬性值,然后從該初始的屬性值漸變到終止值。
- ofInt(Object target, String propertyName, int… values)
參見(jiàn)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(); }
效果圖參見(jiàn)ValueAnimator中對(duì)應(yīng)的圖片。
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();
AnimatorSet
AnimatorSet繼承自Animator。AnimatorSet表示的是動(dòng)畫(huà)的集合,我們可以通過(guò)AnimatorSet把多個(gè)動(dòng)畫(huà)集合在一起,讓其串行或并行執(zhí)行,從而創(chuàng)造出復(fù)雜的動(dòng)畫(huà)效果。
我們想讓TextView先進(jìn)行旋轉(zhuǎn),然后進(jìn)行平移,最后進(jìn)行伸縮,我們可以通過(guò)AnimatorSet實(shí)現(xiàn)該效果,代碼如下所示:
``` //anim1實(shí)現(xiàn)TextView的旋轉(zhuǎn)動(dòng)畫(huà) Animator anim1 = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f); anim1.setDuration(2000); //anim2和anim3TextView的平移動(dòng)畫(huà) Animator anim2 = ObjectAnimator.ofFloat(textView, "translationX", 0f, 300f); anim2.setDuration(3000); Animator anim3 = ObjectAnimator.ofFloat(textView, "translationY", 0f, 400f); anim3.setDuration(3000); //anim4實(shí)現(xiàn)TextView的伸縮動(dòng)畫(huà) 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();*/ ```
效果如下所示:
動(dòng)畫(huà)anim1用于旋轉(zhuǎn)TextView,anim2用于在X軸方向偏移TextView,anim3用于在Y軸方向偏移TextView,anim4用于縮放TextView。我們?cè)谝陨洗a中提供了三種方式通過(guò)AnimationSet把這四個(gè)動(dòng)畫(huà)組合到一起,第二種方式和第三種方式被注釋起來(lái)了。
其實(shí)有很多種辦法實(shí)現(xiàn)上述效果,這里只介紹一下上述三種方式的思路。
在第一種方式中,調(diào)用了animatorSet.playSequentially(anim1, anim2, anim4),該方法將anim1、anim2以及anim4按順序串聯(lián)起來(lái)放到了animatorSet中,這樣首先會(huì)讓動(dòng)畫(huà)anim1執(zhí)行,anim1執(zhí)行完成后,會(huì)依次執(zhí)行動(dòng)畫(huà)anim2,執(zhí)行完anim2之后會(huì)執(zhí)行動(dòng)畫(huà)anim3。通過(guò)調(diào)用animatorSet.playTogether(anim2, anim3),保證了anim2和anim3同時(shí)執(zhí)行,即動(dòng)畫(huà)anim1完成之后會(huì)同時(shí)運(yùn)行anim2和anim3。
在第二種方式中,我們首先創(chuàng)建了一個(gè)AnimatorSet變量anim23,然后通過(guò)anim23.playTogether(anim2, anim3)將anim2和anim3組合成一個(gè)小的動(dòng)畫(huà)集合。然后我們?cè)侔補(bǔ)nim1、anim23以及anim4一起傳入到animatorSet.playSequentially(anim1, anim23, anim4)中,這樣anim1、anim23、anim4會(huì)依次執(zhí)行,而anim23中的anim2和anim3會(huì)同時(shí)執(zhí)行。該方式同時(shí)也演示了可以將一個(gè)AnimatorSet作為動(dòng)畫(huà)的一部分放入另一個(gè)AnimatorSet中。
在第三種方式中,我們使用了AnimatorSet的play方法,該方法返回AnimatorSet.Builder類型,animatorSet.play(anim1).before(anim2)確保了anim1執(zhí)行完了之后執(zhí)行anim2,animatorSet.play(anim2).with(anim3)確保了anim2和anim3同時(shí)執(zhí)行,animatorSet.play(anim4).after(anim2)確保了anim2執(zhí)行完了之后執(zhí)行anim4。需要說(shuō)明的是animatorSet.play(anim1).before(anim2)與animatorSet.play(anim2).after(anim1)是完全等價(jià)的,之所以在上面代碼中有的寫(xiě)before,有的寫(xiě)after,只是為了讓大家多了解一下API。
希望本文對(duì)大家學(xué)習(xí)屬性動(dòng)畫(huà)有所幫助。
- Android屬性動(dòng)畫(huà)實(shí)現(xiàn)炫酷的登錄界面
- Android屬性動(dòng)畫(huà)實(shí)現(xiàn)布局的下拉展開(kāi)效果
- Android動(dòng)畫(huà) 實(shí)現(xiàn)開(kāi)關(guān)按鈕動(dòng)畫(huà)(屬性動(dòng)畫(huà)之平移動(dòng)畫(huà))實(shí)例代碼
- Android 屬性動(dòng)畫(huà)ValueAnimator與插值器詳解
- Android 自定義view和屬性動(dòng)畫(huà)實(shí)現(xiàn)充電進(jìn)度條效果
- Android 動(dòng)畫(huà)(View動(dòng)畫(huà),幀動(dòng)畫(huà),屬性動(dòng)畫(huà))詳細(xì)介紹
- Android模擬開(kāi)關(guān)按鈕點(diǎn)擊打開(kāi)動(dòng)畫(huà)(屬性動(dòng)畫(huà)之平移動(dòng)畫(huà))
- Android屬性動(dòng)畫(huà)之ValueAnimator代碼詳解
- Android屬性動(dòng)畫(huà)實(shí)現(xiàn)圖片從左到右逐漸消失
- Android動(dòng)畫(huà)系列之屬性動(dòng)畫(huà)的基本使用教程
相關(guān)文章
Android模擬用戶點(diǎn)擊的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android模擬用戶點(diǎn)擊的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)學(xué)習(xí)學(xué)習(xí)吧。2018-02-02Android6.0獲取GPS定位和獲取位置權(quán)限和位置信息的方法
今天小編就為大家分享一篇Android6.0獲取GPS定位和獲取位置權(quán)限和位置信息的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Android編程之短信列表的時(shí)間顯示實(shí)例分析
這篇文章主要介紹了Android編程之短信列表的時(shí)間顯示,實(shí)例分析了Android中短信列表的時(shí)間顯示及具體注意事項(xiàng),具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android基于廣播事件機(jī)制實(shí)現(xiàn)簡(jiǎn)單定時(shí)提醒功能代碼
這篇文章主要介紹了Android基于廣播事件機(jī)制實(shí)現(xiàn)簡(jiǎn)單定時(shí)提醒功能代碼,較為詳細(xì)的分析了Android廣播事件機(jī)制及提醒功能的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10