android?Animator 講解【上】
目錄
1.簡(jiǎn)介
2.屬性動(dòng)畫相對(duì)補(bǔ)間動(dòng)畫和幀動(dòng)畫好處
3.ValueAnimator 屬性動(dòng)畫
4.ObjectAnimator 屬性動(dòng)畫
5.AnimatorSet組合動(dòng)畫
6.使用XML編寫動(dòng)畫
7.動(dòng)畫監(jiān)聽器
簡(jiǎn)介
使用安卓手機(jī)的朋友的都知道碉渡,手機(jī)上面有一些效果比較炫酷娃豹。因?yàn)橄到y(tǒng)在一開始的時(shí)候就給我們提供了兩種實(shí)現(xiàn)動(dòng)畫效果的方式:
? 逐幀動(dòng)畫(frame-by-frame animation)
? 補(bǔ)間動(dòng)畫(tweened animation)
逐幀動(dòng)畫的理解比較簡(jiǎn)單绳泉,其實(shí)就是把一個(gè)完整的動(dòng)畫拆分成一張張圖片,然后再把他們串聯(lián)起來進(jìn)行播放,有點(diǎn)像看視頻一樣
補(bǔ)間動(dòng)畫是可以進(jìn)行對(duì)View進(jìn)行一系列的動(dòng)畫操作,包括淡入淡出、縮放瞻离、平移、旋轉(zhuǎn)4種效果乒裆。
在Android3.0之后官方給出一種全新的動(dòng)畫模式,屬性動(dòng)畫(property animation),它的功能非常強(qiáng)大鹤耍,彌補(bǔ)了之前補(bǔ)間動(dòng)畫的一些缺陷肉迫,幾乎是可以完全替代掉補(bǔ)間動(dòng)畫了。
屬性動(dòng)畫相對(duì)補(bǔ)間動(dòng)畫和幀動(dòng)畫好處
Android之前推存的補(bǔ)間動(dòng)畫是相對(duì)于View來就行操作的稿黄,比如說對(duì)view進(jìn)行移動(dòng)喊衫、縮放、旋轉(zhuǎn)杆怕、淡入淡出等常見效果族购。但是如果我們的需求超出這寫操作,那么補(bǔ)間動(dòng)畫就不能滿足我們了陵珍。補(bǔ)間動(dòng)畫的擴(kuò)展性能是有相當(dāng)大的局限性的寝杖,既然補(bǔ)間動(dòng)畫不能勝任需求。那么我們就該換另一種更有滿足我們擴(kuò)展性能更好的動(dòng)畫了互纯。
屬性動(dòng)畫
為什么屬性動(dòng)畫能勝任我們的需求呢瑟幕?
相對(duì)補(bǔ)間動(dòng)畫的效果,比如我對(duì)這個(gè)view 就行點(diǎn)擊事件監(jiān)聽留潦,原位置是(10,10)只盹,但是我在該為進(jìn)行了補(bǔ)間動(dòng)畫的移動(dòng)一個(gè)位置(100,100),當(dāng)我們?nèi)c(diǎn)事該vew的時(shí)候兔院,事件不會(huì)被觸發(fā)殖卑,點(diǎn)擊原來的位置(10,10)事件被觸發(fā)了。
為什么會(huì)這樣坊萝?
因?yàn)檠a(bǔ)間動(dòng)畫只是改變了view的顯示效果孵稽,但是并沒有改變?cè)搗iew位置狀態(tài),view控件依然在位置(10屹堰,10)中肛冶。
Android3.0推存出來的屬性動(dòng)畫,不僅能夠?qū)崿F(xiàn)補(bǔ)間動(dòng)畫的效果扯键,還能填補(bǔ)補(bǔ)間動(dòng)畫的不足睦袖。屬性動(dòng)畫是相對(duì)于屬性來改變View的,所以上面的移動(dòng)到(100荣刑,100)位置馅笙,view的 X和Y屬性自然也是100了,所以當(dāng)你點(diǎn)擊該vewi的時(shí)候厉亏,事件在位置(100董习,100)觸發(fā)了,在位置(10爱只,10)沒有被觸發(fā)皿淋!
ValueAnimator 屬性動(dòng)畫
ValueAnimator是整個(gè)屬性動(dòng)畫機(jī)制當(dāng)中最核心的一個(gè)類,前面我們已經(jīng)提到了,屬性動(dòng)畫的運(yùn)行機(jī)制是通過不斷地對(duì)值進(jìn)行操作來實(shí)現(xiàn)的窝趣,而初始值和結(jié)束值之間的動(dòng)畫過渡就是由ValueAnimator這個(gè)類來負(fù)責(zé)計(jì)算的疯暑。它的內(nèi)部使用一種時(shí)間循環(huán)的機(jī)制來計(jì)算值與值之間的動(dòng)畫過渡,我們只需要將初始值和結(jié)束值提供給ValueAnimator哑舒,并且告訴它動(dòng)畫所需運(yùn)行的時(shí)長(zhǎng)妇拯,那么ValueAnimator就會(huì)自動(dòng)幫我們完成從初始值平滑地過渡到結(jié)束值這樣的效果。除此之外洗鸵,ValueAnimator還負(fù)責(zé)管理動(dòng)畫的播放次數(shù)越锈、播放模式、以及對(duì)動(dòng)畫設(shè)置監(jiān)聽器等膘滨,確實(shí)是一個(gè)非常重要的類甘凭。
還有ValueAnimator的用法卻一點(diǎn)都不復(fù)雜,我們先從最簡(jiǎn)單的功能看起吧吏祸,比如說想要將一個(gè)值從0平滑過渡到1对蒲,時(shí)長(zhǎng)100毫秒,就可以這樣寫:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(100);
anim.start();
很簡(jiǎn)單的代碼就可以構(gòu)建出一個(gè)屬性動(dòng)畫實(shí)例贡翘。設(shè)置的動(dòng)畫是100我們來看一下ofFloat源碼
public static ValueAnimator ofFloat(float... values) {ValueAnimator anim =new ValueAnimator();? ? anim.setFloatValues(values);? ? return anim;}
可以看出蹈矮,這里的ofFloat方法里面可以傳入很多個(gè)float,是以數(shù)組的形式的一個(gè)參數(shù)鸣驱。如下代碼:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
// 監(jiān)聽變化狀態(tài)
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) {
// 打印 log?animation.getAnimatedValue() ?
?}});
setAnimatorListener(anim, "沒有添加控件狀態(tài)下測(cè)試ofInt");anim.start();
如圖所示:
從打印的值中泛鸟,我們可以看出數(shù)值是從0到1變大的,而且增加不是均勻的踊东。當(dāng)然我們也可以設(shè)置均勻的增加北滥,不過這個(gè)知識(shí)點(diǎn)劉下一章在講解。
在ValueAnimator中闸翅,我們還可以調(diào)用setStartDelay()方法來設(shè)置動(dòng)畫延遲播放的時(shí)間再芋,調(diào)用setRepeatCount()和setRepeatMode()方法來設(shè)置動(dòng)畫循環(huán)播放的次數(shù)以及循環(huán)播放的模式,循環(huán)模式包括RESTART和REVERSE兩種坚冀,分別表示重新播放和倒序播放的意思济赎。需要連接的可以百度一下,因?yàn)檫@已經(jīng)是很成熟的技術(shù)了记某。
ObjectAnimator屬性動(dòng)畫
ObjectAnimator 是?ValueAnimator的子類司训,也是我們?cè)陂_發(fā)中接觸最多的一個(gè)類。既然是ValueAnimator的子類液南,那么ValueAnimator可以用的方法ObjectAnimator 當(dāng)然也可以用了壳猜,ObjectAnimator 也是很好用的。比如想把一個(gè)TextView做淡入淡出的效果滑凉,效果是這樣子的:不透明 》全透明》不透明》半透明》透明》不透明
ObjectAnimator anim = ObjectAnimator
? ? ? ? ? ? .ofFloat(textView, "alpha", 1f, 0f, 1f, 0.5f, 0f, 1f);
anim.setDuration(3000);
anim.start();
效果如下:
第一個(gè)參數(shù)是 給定的控件 View 统扳,第二個(gè)參數(shù)可能很多人不理解喘帚,其實(shí)這個(gè)參數(shù)是可以隨意起的,就是你可以隨意傳任何對(duì)象闪幽,他所負(fù)責(zé)的工作就是不斷的向你起的這個(gè)屬性對(duì)象進(jìn)行賦值啥辨,然后根據(jù)屬性值的改變?cè)谌绾握故境鰜淼摹?/p>
就拿剛剛的這個(gè)來說
ObjectAnimator.ofFloat(textView, "alpha", 1f, 0f, 1f, 0.5f, 0f, 1f);
textView是沒有存在這個(gè)?alpha 屬性的,它的父類View也是一樣沒有這個(gè)?alpha 屬性盯腌。是不是奇怪,很有屬性怎么又變成了是屬性對(duì)象陨瘩?別急腕够,慢慢跟大家分析,其實(shí)這個(gè)?alpha 屬性不是根據(jù)View中有沒有這個(gè)屬性的舌劳,而是根據(jù) view中是否存在?alpha 的set和get方法的帚湘,我們?cè)O(shè)置的動(dòng)畫也是一樣哦,設(shè)置?alpha 就是為了去 view中尋找?alpha對(duì)應(yīng)的?setAlpha 和?getAlpha 方法甚淡。不信的同學(xué)可以在 View中進(jìn)行查詢哦大诸。
第三個(gè)參數(shù)就不用多說了吧 ,上面有提到他是一個(gè)數(shù)組形式的參數(shù)贯卦,可以傳多個(gè)對(duì)應(yīng)的類型執(zhí)行動(dòng)畫效果资柔。
根據(jù)上面的第二個(gè)參數(shù)是可以在View中獲取set、get方法的撵割,那么如果第二個(gè)參數(shù)是:rotation贿堰、translationX、translationY啡彬、scaleX羹与、scaleY也是一樣可以在View中找到他們的set和get方法的。說了那么多庶灿,為什么這個(gè)參數(shù)是這樣子的纵搁,可以自己命名嗎? 答案是肯定的往踢,這個(gè)參數(shù)也可以自定義自己的一個(gè)屬性名字腾誉,上面提到過,而剛剛說的那些?alpha菲语、rotation妄辩、translationX、translationY山上、scaleX眼耀、scaleY 都是系統(tǒng)View中自帶的set、get方法佩憾,也是一樣沒有這個(gè)屬性名哮伟,只有對(duì)應(yīng)的方法名干花。
如果我們要想自己定義屬性,下一章我們?cè)谥攸c(diǎn)學(xué)習(xí)楞黄。
AnimatorSet組合動(dòng)畫
什么是組合動(dòng)畫呢池凄?
組合動(dòng)畫就是能實(shí)現(xiàn)將多個(gè)動(dòng)畫組合到一起播放!
實(shí)現(xiàn)組合動(dòng)畫功能主要需要借助AnimatorSet這個(gè)類鬼廓,這個(gè)類提供了一個(gè)play()方法肿仑,如果我們向這個(gè)方法中傳入一個(gè)Animator對(duì)象(ValueAnimator或ObjectAnimator)將會(huì)返回一個(gè)AnimatorSet.Builder的實(shí)例,AnimatorSet.Builder中包括以下四個(gè)方法:
? ? after(Animator anim) ? 將現(xiàn)有動(dòng)畫插入到傳入的動(dòng)畫之后執(zhí)行
? ? after(long delay) ? 將現(xiàn)有動(dòng)畫延遲指定毫秒后執(zhí)行
? ? before(Animator anim) ? 將現(xiàn)有動(dòng)畫插入到傳入的動(dòng)畫之前執(zhí)行
? ? with(Animator anim) ? 將現(xiàn)有動(dòng)畫和傳入的動(dòng)畫同時(shí)執(zhí)行
看一下幾個(gè)方法實(shí)現(xiàn)的效果:
ObjectAnimator anim1 = ObjectAnimator
? ? ? ? .ofFloat(mTest, "translationX", 0f, 400f, -50f, 270f, -150f, 100f, 0f);ObjectAnimator anim2 = ObjectAnimator
? ? ? ? .ofFloat(mTest, "translationY", 0f, 400f, -50f, 270f, -150f, 100f, 0f);ObjectAnimator anim3 = ObjectAnimator
? ? ? ? .ofFloat(mTest, "rotation", 0f, 360f, -180f, 270f, 0f);ObjectAnimator anim4 = ObjectAnimator
? ? ? ?? .ofFloat(mTest, "scaleX", 1f, 3f, 2f, 1f);AnimatorSet animSet =new AnimatorSet();
animSet.play(anim1).with(anim2).after(anim3).before(anim4);
animSet.setDuration(5000);
animSet.start();
動(dòng)畫執(zhí)行順序:anim3 》anim2+anim1》anim4
ObjectAnimator anim1 = ObjectAnimator
? ? ? ? ? .ofFloat(mTest, "translationX", 0f, 400f, -50f, 270f, -150f, 100f, 0f);ObjectAnimator anim2 = ObjectAnimator
? ? ? ?? .ofFloat(mTest, "translationY", 0f, 400f, -50f, 270f, -150f, 100f, 0f);ObjectAnimator anim3 = ObjectAnimator
? ? ? ?? .ofFloat(mTest, "rotation", 0f, 360f, -180f, 270f, 0f);AnimatorSet animSet =new AnimatorSet();
animSet.play(anim1).with(anim2).with(anim3);
animSet.setDuration(5000);
animSet.start();
動(dòng)畫執(zhí)行順序:anim1+anim2+anim3 同時(shí)執(zhí)行
使用XML編寫動(dòng)畫
除了代碼編寫動(dòng)畫碎税,其實(shí)我們也可以在XML中編寫動(dòng)畫尤慰。首先我們需要在res中新建一個(gè)文件夾?animator 來存儲(chǔ)我們編寫的動(dòng)畫。
在XML中雷蹂,我們有三種標(biāo)簽是可以用來編寫動(dòng)畫的:
??< animator > 對(duì)應(yīng)代碼中的ValueAnimator
?< objectAnimator?> 對(duì)應(yīng)代碼中的ObjectAnimator
??< set > 對(duì)應(yīng)代碼中的AnimatorSet
好了伟端,我們來實(shí)現(xiàn)一個(gè)從0到300平滑過渡的動(dòng)畫,在XML當(dāng)中就可以這樣寫:
<animator?xmlns:android="http://schemas.android.com/apk/res/android" ?
android:valueFrom="0" ?
android:valueTo="300" ?
android:valueType="intType" />
想將一個(gè)視圖的alpha屬性2秒內(nèi)從1變成0匪煌,就可以這樣寫:
<objectAnimator?xmlns:android="http://schemas.android.com/apk/res/android" ?
android:duration="2000" ?
android:valueFrom="1" ?
android:valueTo="0" ?
android:valueType="floatType" ?
android:propertyName="alpha" />
用 set 標(biāo)簽組合上面的動(dòng)畫就可以這樣子寫:
< set?xmlns:android="http://schemas.android.com/apk/res/android"
?android:ordering="sequentially"?> ?< objectAnimator ?
? ? ?android:duration="2000" ?
? ? ?android:propertyName="alpha" ?
? ? ?android:valueFrom="1" ?
? ? ?android:valueTo="0" ?
? ? ?android:valueType="floatType" ?/ > ?< objectAnimator ?
? ? ?android:duration="2000" ?
? ? ?android:valueFrom="0" ?
? ? ?android:valueTo="300" ?
? ? ?android:propertyName="translationX?"
? ? ?android:valueType="intType" / > ?</set>
動(dòng)畫監(jiān)聽器
動(dòng)畫監(jiān)聽器是可以隨時(shí)隨地的監(jiān)聽當(dāng)前動(dòng)畫執(zhí)行變化狀態(tài)的责蝠。監(jiān)聽器的功能:監(jiān)聽動(dòng)畫開始前,動(dòng)畫結(jié)束萎庭,動(dòng)畫當(dāng)前執(zhí)行的狀態(tài)都可以獲取霜医。Animator類當(dāng)中就是提供了一個(gè)addListener()方法來實(shí)現(xiàn)這寫效果的,這個(gè)方法接收一個(gè)AnimatorListener擎椰,我們只需要去實(shí)現(xiàn)這個(gè)AnimatorListener就可以監(jiān)聽動(dòng)畫的各種事件了支子。
該監(jiān)聽器實(shí)現(xiàn)AnimatorListener接口有四個(gè)方法:
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:動(dòng)畫開始的時(shí)候調(diào)用
onAnimationRepeat()方法會(huì)在動(dòng)畫重復(fù)執(zhí)行的時(shí)候調(diào)用
onAnimationEnd()方法會(huì)在動(dòng)畫結(jié)束的時(shí)候調(diào)用
onAnimationCancel()方法會(huì)在動(dòng)畫被取消的時(shí)候調(diào)用。
有時(shí)候我們沒有必要實(shí)現(xiàn)那么事件方法达舒,只需要四個(gè)方法中的某一個(gè)值朋,這時(shí)候Android提供了一個(gè)適配器類,叫作AnimatorListenerAdapter巩搏,使用這個(gè)類就可以解決掉實(shí)現(xiàn)接口繁瑣的問題了昨登,如下所示:
anim.addListener(new?AnimatorListenerAdapter()?{ ?
});?
這里我們向addListener()方法中傳入這個(gè)適配器對(duì)象,由于AnimatorListenerAdapter中已經(jīng)將每個(gè)接口都實(shí)現(xiàn)好了贯底,所以這里不用實(shí)現(xiàn)任何一個(gè)方法也不會(huì)報(bào)錯(cuò)丰辣。那么如果我想監(jiān)聽動(dòng)畫結(jié)束這個(gè)事件,就只需要單獨(dú)重寫這一個(gè)方法就可以了禽捆,如下所示:
anim.addListener(new?AnimatorListenerAdapter()?{ ?
@Override ?
public?void?onAnimationEnd(Animator?animation)?{ ?
? ? } ?
});?
好了笙什,本篇就在此完結(jié)了,下篇文章我們重點(diǎn)解析屬性動(dòng)畫的類型和插值器