一.屬性動(dòng)畫簡介
屬性動(dòng)畫非常強(qiáng)大形耗,它的學(xué)習(xí)我是參考了這篇文章,博主寫的非常獨(dú)到辙浑、透徹激涤、詳細(xì),在此感謝博主 Android屬性動(dòng)畫完全解析(上)判呕,初識(shí)屬性動(dòng)畫的基本用法 ,以下引用均出自該篇文章倦踢。
然而自Android 3.0版本開始,系統(tǒng)給我們提供了一種全新的動(dòng)畫模式侠草,屬性動(dòng)畫(property animation)辱挥,它的功能非常強(qiáng)大,彌補(bǔ)了之前補(bǔ)間動(dòng)畫的一些缺陷梦抢,幾乎是可以完全替代掉補(bǔ)間動(dòng)畫了般贼。
Tween動(dòng)畫的缺點(diǎn)歸納(為什么誕生屬性動(dòng)畫):
- 一旦我們的需求超出了移動(dòng)愧哟、縮放奥吩、旋轉(zhuǎn)和淡入淡出這四種對(duì)View的操作,那么補(bǔ)間動(dòng)畫就不能再幫我們忙了蕊梧,也就是說它在功能和可擴(kuò)展方面都有相當(dāng)大的局限性霞赫,那么下面我們就來看看補(bǔ)間動(dòng)畫所不能勝任的場景。就是它只能夠?qū)崿F(xiàn)移動(dòng)肥矢、縮放端衰、旋轉(zhuǎn)和淡入淡出這四種動(dòng)畫操作,那如果我們希望可以對(duì)View的背景色進(jìn)行動(dòng)態(tài)地改變呢甘改?
- 介紹補(bǔ)間動(dòng)畫的時(shí)候都有使用“對(duì)View進(jìn)行操作”這樣的描述旅东,沒錯(cuò),補(bǔ)間動(dòng)畫是只能夠作用在View上的十艾。也就是說抵代,我們可以對(duì)一個(gè)Button、TextView忘嫉、甚至是LinearLayout荤牍、或者其它任何繼承自View的組件進(jìn)行動(dòng)畫操作案腺,但是如果我們想要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫操作,抱歉康吵,補(bǔ)間動(dòng)畫就幫不上忙了
- 補(bǔ)間動(dòng)畫還有一個(gè)致命的缺陷劈榨,就是它只是改變了View的顯示效果而已,而不會(huì)真正去改變View的屬性晦嵌。
對(duì)于歸納出的缺點(diǎn)的第2點(diǎn)同辣,可能不太讓人理解,為什么對(duì)非View對(duì)象進(jìn)行動(dòng)畫操作呢耍铜?
我怎么會(huì)需要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫操作呢邑闺?這里我舉一個(gè)簡單的例子,比如說我們有一個(gè)自定義的View棕兼,在這個(gè)View當(dāng)中有一個(gè)Point對(duì)象用于管理坐標(biāo)陡舅,然后在onDraw()方法當(dāng)中就是根據(jù)這個(gè)Point對(duì)象的坐標(biāo)值來進(jìn)行繪制的。也就是說伴挚,如果我們可以對(duì)Point對(duì)象進(jìn)行動(dòng)畫操作靶衍,那么整個(gè)自定義View的動(dòng)畫效果就有了。顯然茎芋,補(bǔ)間動(dòng)畫是不具備這個(gè)功能的颅眶,這是它的一個(gè)缺陷。
對(duì)于歸納出的第3點(diǎn)田弥,也讓人疑問涛酗,什么叫沒有改變View的屬性
比如說,現(xiàn)在屏幕左上角有一個(gè)按鈕偷厦,然后我們通過補(bǔ)間動(dòng)畫將它移動(dòng)到了屏幕右下角商叹,現(xiàn)在你可以去嘗試點(diǎn)擊一下這個(gè)處于屏幕右下角的按鈕(動(dòng)畫執(zhí)行過后的按鈕),點(diǎn)擊事件是絕對(duì)不會(huì)觸發(fā)的只泼,因?yàn)閷?shí)際上這個(gè)按鈕還是停留在屏幕的左上角剖笙,只不過補(bǔ)間動(dòng)畫將這個(gè)按鈕繪制到了屏幕的右下角而已。也就是說Tween動(dòng)畫只是魔術(shù)请唱,一切只是虛像弥咪。
驚喜的是屬性動(dòng)畫完全解決了以上的問題。是不是很強(qiáng)大J蟆>壑痢!1境取扳躬!但這不意味著我們拋棄了Tween動(dòng)畫。
二.屬性動(dòng)畫整體結(jié)構(gòu)
我們先不究其細(xì)節(jié),現(xiàn)在整體初識(shí)它坦报,這樣學(xué)習(xí)起來更有全面感库说。它位于android.animation包下。
屬性動(dòng)畫機(jī)制已經(jīng)不再是針對(duì)于View來設(shè)計(jì)的了片择,也不限定于只能實(shí)現(xiàn)移動(dòng)潜的、縮放、旋轉(zhuǎn)和淡入淡出這幾種動(dòng)畫操作字管,同時(shí)也不再只是一種視覺上的動(dòng)畫效果了啰挪。它實(shí)際上是一種不斷地對(duì)值進(jìn)行操作的機(jī)制,并將值賦值到指定對(duì)象的指定屬性上嘲叔,可以是任意對(duì)象的任意屬性亡呵。所以我們?nèi)匀豢梢詫⒁粋€(gè)View進(jìn)行移動(dòng)或者縮放,但同時(shí)也可以對(duì)自定義View中的Point對(duì)象進(jìn)行動(dòng)畫操作了硫戈。我們只需要告訴系統(tǒng)動(dòng)畫的運(yùn)行時(shí)長锰什,需要執(zhí)行哪種類型的動(dòng)畫,以及動(dòng)畫的初始值和結(jié)束值丁逝,剩下的工作就可以全部交給系統(tǒng)去完成了汁胆。
工作原理
Property Animation動(dòng)畫有兩個(gè)步聚:
1.計(jì)算屬性值
2.為目標(biāo)對(duì)象的屬性設(shè)置屬性值,即應(yīng)用和刷新動(dòng)畫
XML的屬性動(dòng)畫的應(yīng)用
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
它有以下幾個(gè)核心類以及對(duì)應(yīng)的繼承關(guān)系
三.屬性動(dòng)畫細(xì)節(jié)分析
(1)ValueAnimator 類
1.介紹
屬性動(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í)長铸题,那么ValueAnimator就會(huì)自動(dòng)幫我們完成從初始值平滑地過渡到結(jié)束值這樣的效果。除此之外琢感,ValueAnimator還負(fù)責(zé)管理動(dòng)畫的播放次數(shù)丢间、播放模式、以及對(duì)動(dòng)畫設(shè)置監(jiān)聽器等猩谊,確實(shí)是一個(gè)非常重要的類千劈。
2.小案例
eg.有以下案例(但是這些動(dòng)畫無任何效果可看到祭刚,但是可以通過日志看到值變化情況)
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
//ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);
//ValueAnimator anim2 = ValueAnimator.ofInt(0, 100);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
(1)anim:將一個(gè)值從0平滑過渡到1牌捷,時(shí)長300毫秒,日志如下:
(2)anim1:將一個(gè)值在5秒內(nèi)從0過渡到5涡驮,再過渡到3暗甥,再過渡到10
(3)anim2:當(dāng)你不需要小數(shù)位數(shù)的動(dòng)畫過渡,可能你只是希望將一個(gè)整數(shù)值從0平滑地過渡到100捉捅,就可以用ofInt
講解:
- 調(diào)用ValueAnimator的ofFloat()方法就可以構(gòu)建出一個(gè)ValueAnimator的實(shí)例撤防,ofFloat()方法當(dāng)中允許傳入多個(gè)float類型的參數(shù),這里傳入0和1就表示將值從0平滑過渡到1棒口,然后調(diào)用ValueAnimator的setDuration()方法來設(shè)置動(dòng)畫運(yùn)行的時(shí)長寄月,最后調(diào)用start()方法啟動(dòng)動(dòng)畫辜膝。
- ValueAnimator當(dāng)中最常用的應(yīng)該就是ofFloat()和ofInt()這兩個(gè)方法了,另外還有一個(gè)ofObject()方法漾肮。那么除此之外厂抖,我們還可以調(diào)用setStartDelay()方法來設(shè)置動(dòng)畫延遲播放的時(shí)間,調(diào)用setRepeatCount()和setRepeatMode()方法來設(shè)置動(dòng)畫循環(huán)播放的次數(shù)以及循環(huán)播放的模式克懊,循環(huán)模式包括RESTART和REVERSE兩種忱辅,分別表示重新播放和倒序播放的意思。
同時(shí)修改多個(gè)屬性值
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "xxx", 1.0F, 0.0F)
.setDuration(500);
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float cVal = (Float) animation.getAnimatedValue();
view.setAlpha(cVal);
view.setScaleX(cVal);
view.setScaleY(cVal);
}
});
4.對(duì)應(yīng)XML的<animator>
實(shí)現(xiàn)一個(gè)從0到100平滑過渡的動(dòng)畫谭溉,在XML當(dāng)中就可以這樣寫:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType"/>
(2)ObjectAnimator-可以直接對(duì)任意對(duì)象的任意屬性進(jìn)行動(dòng)畫操作
1.介紹
ValueAnimator只不過是對(duì)值進(jìn)行了一個(gè)平滑的動(dòng)畫過渡墙懂,但是ObjectAnimator則就不同了,它是可以直接對(duì)任意對(duì)象的任意屬性進(jìn)行動(dòng)畫操作的扮念,比如說View的alpha屬性损搬。但是它其實(shí)是繼承自ValueAnimator的,底層的動(dòng)畫實(shí)現(xiàn)機(jī)制也是基于ValueAnimator來完成的柜与,因此ValueAnimator仍然是整個(gè)屬性動(dòng)畫當(dāng)中最核心的一個(gè)類场躯。那么既然是繼承關(guān)系,說明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的旅挤,它們的用法也非常類似
2.小案例
eg.如果我們想要將一個(gè)TextView的某一個(gè)屬性進(jìn)行操作踢关,就可以這樣寫:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
//ObjectAnimator animator1 = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
/**
float curTranslationX = textview.getTranslationX();
ObjectAnimator animator2 = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);
**/
//ObjectAnimator animator3 = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);
animator.setDuration(5000);
animator.start();
(1)animator :TextView在5秒中內(nèi)從常規(guī)變換成全透明,再從全透明變換成常規(guī)
(2)animator1 :TextView進(jìn)行一次360度的旋轉(zhuǎn)
(3)animator2 :TextView先向左移出屏幕粘茄,然后再移動(dòng)回來
(4)animator3 :TextView在垂直方向上放大3倍再還原
3.講解:
ObjectAnimator的ofFloat參數(shù)應(yīng)該要填什么呢签舞?也就是說修改TextView的透明度,為什么知道要填寫“alpha”呢柒瓣?就是要去查看一個(gè)參數(shù)對(duì)象TextView儒搭,里面是否提供set/get方法,比如TextView有setAlpha方法芙贫,有setTranslationX搂鲫,那么對(duì)應(yīng)第二個(gè)參數(shù)就填寫該屬性。其實(shí)這些方法是由View對(duì)象提供的磺平,也就是說不僅TextView可以使用這個(gè)屬性來進(jìn)行淡入淡出動(dòng)畫操作魂仍,任何繼承自View的對(duì)象都可以的。所以View方法里必然有那么View當(dāng)中一定也存在著setRotation()拣挪、getRotation()擦酌、setTranslationX()、getTranslationX()菠劝、setScaleY()赊舶、getScaleY()這些方法
得出結(jié)論:
ObjectAnimator內(nèi)部的工作機(jī)制是通過尋找特定屬性的get和set方法,然后通過方法不斷地對(duì)值進(jìn)行改變,從而實(shí)現(xiàn)動(dòng)畫效果的笼平。
4.對(duì)應(yīng)XML的<objectAnimator>
將一個(gè)視圖的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"/>
(3)AnimatorSet - 組合動(dòng)畫
1.介紹
實(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í)行
A.after(B):A要在B之后執(zhí)行
A.before(B):A要在B之前執(zhí)行
A.with(B):A和B同時(shí)執(zhí)行
2.小案例
TextView先從屏幕外移動(dòng)進(jìn)屏幕,然后開始旋轉(zhuǎn)360度捶牢,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作鸠珠,就可以這樣寫:
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
3.講解
可以看到,這里我們先是把三個(gè)動(dòng)畫的對(duì)象全部創(chuàng)建出來秋麸,然后new出一個(gè)AnimatorSet對(duì)象之后將這三個(gè)動(dòng)畫對(duì)象進(jìn)行播放排序渐排,讓旋轉(zhuǎn)和淡入淡出動(dòng)畫同時(shí)進(jìn)行,并把它們插入到了平移動(dòng)畫的后面灸蟆,最后是設(shè)置動(dòng)畫時(shí)長以及啟動(dòng)動(dòng)畫驯耻。
4.對(duì)應(yīng)XML的<set>
將一個(gè)視圖先從屏幕外移動(dòng)進(jìn)屏幕,然后開始旋轉(zhuǎn)360度炒考,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作可缚,就可以這樣寫:
<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>
(4)Animator各種監(jiān)聽器
1.AnimatorListener
通過addListener()添加進(jìn)去
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()方法會(huì)在動(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)用。
AnimatorListenerAdapter
也許很多時(shí)候我們并不想要監(jiān)聽那么多個(gè)事件瓤帚,可能我只想要監(jiān)聽動(dòng)畫結(jié)束這一個(gè)事件描姚,那么每次都要將四個(gè)接口全部實(shí)現(xiàn)一遍就顯得非常繁瑣。沒關(guān)系戈次,為此Android提供了一個(gè)適配器類轩勘,叫作AnimatorListenerAdapter,使用這個(gè)類就可以解決掉實(shí)現(xiàn)接口繁瑣的問題了
anim.addListener(new AnimatorListenerAdapter() {
/**@Override
public void onAnimationEnd(Animator animation) {
} **/
});