【回復「1024」,送你一個特別推送】
投稿作者:奮鬥之路
原文連結:http://blog.csdn.net/dmk877/article/details/52011155
特別聲明:本文為奮鬥之路原創並授權發布,未經原作者允許請勿轉載,轉載請聯繫原作者。
Hello,大家好,今天又來裝逼了,裝逼也上癮啊,最近公司不是特別忙,我想這也就是我出來裝逼的最好時機吧!額,,哈哈,進入正題。如有疑問歡迎留言,如有謬誤歡迎批評指正。
在Tween動畫的討論中,我們提到在Android中動畫可以分為三類:①幀動畫②Tween(補間動畫)③Property Animation(屬性動畫),在前面的文章中,分別對幀動畫和Tween動畫進行了非常詳細的討論,如果有興趣可以去上面的連結去閱讀。那麼今天就來和大家一起討論下Property Animation,相信通過本系列博客的討論你將對Android中的動畫有個非常詳細的了解。
通過本篇博客你將學到以下內容:
①為什麼要引入屬性動畫
②屬性動畫的基本用法
③屬性動畫的監聽器
④組合動畫的實現
⑤屬性動畫的XML實現
首先來看為什麼要引入屬性動畫,我相信很多人跟我一樣,看到屬性動畫,在腦海裡閃現的第一個問題就是為什麼要引入屬性動畫?我們都知道Android中已經有幀動畫和補間動畫了,那麼為什麼還要引入屬性動畫呢?要想得到這個問題的正確答案,無疑要去谷歌的官網了,首先我們來看看官網(官網地址)對Property Animation與補間動畫的區別進行的介紹:
補間動畫只提供了對View進行增加動畫的能力,所以如果你想對除View之外的其它對象做動畫,你必須實現你自己的代碼來達到這樣的效果。另外,補間動畫只能對View的幾個方面進行動畫的添加,例如View的縮放和旋轉,而不是View的背景顏色等等。
補間動畫的另一個缺點是它只修改了視圖繪製的地方,而不是實際View的本身。比如,你給Button定義了一個在屏幕上移動的動畫,這個Button的繪製是正確的,但是這個Button實際可以點擊的位置是沒有改變的,所以你必須用你自己的邏輯來解決這個問題。
使用屬性動畫這些約束將完全被解除,並且你可以對任何對象(Views and non-Views)的任何屬性添加動畫,並且這個對象的本身實際也是改變的。從更高層次上來說,你可以選擇你想要的屬性,來給其添加動畫,如顏色、位置或大小,並且你可以通過插值器或者多個動畫的同步,來定義你所需要的動畫。
然而補間動畫需要較少的時間來設置,並且也需要更少的代碼。如果補間動畫完成了你所需要做的一切或者現有的代碼就是按照你想要的方式工作的,那麼你沒有必要使用屬性動畫。針對不同的情況有時候也許需要這兩種動畫進行工作才是有意義的。
以上三段就是官網給出的屬性動畫與補間動畫的區別,可能看著比較費勁,其實引入屬性動畫主要有三點原因:
①因為補間動畫只能對View進行操作,而不能對一個對象的屬性,如顏色等進行操作,而屬性動畫可以,並且屬性動畫的操作範圍不僅僅是View,它可以是任何對象。
②補間動畫只能對View的幾個方面做動畫,也就是說補間動畫不僅把範圍縮小到View,而且並不是能對View的各個方面做動畫,而只能是alpha(漸變)、scale(縮放)、translate(位移)、rotate(旋轉)這四種動畫。
③補間動畫只是改變了View繪製的地方,而並沒有真正改變View本身。什麼意思?假如手機屏幕上有一個View,我們讓他做補間動畫向右移動20px,我們會看到這個View向右移動了20px,而此時你會發現這個View是不能響應你的點擊事件的,只有你點擊原來的位置才能觸發這個View的點擊事件。因為這個View實際還在原來的位置,只不過補間動畫將這個View繪製的地方向右移動了20px,而這個View真正的屬性並沒有改變。
通過上面的介紹,相信大家已經明白了屬性動畫產生的原因,了解了它產生的背景之後,接下來的一步就是要學習它的用法了。
2、屬性動畫的介紹屬性動畫常用的有兩個類分別是ValueAnimator和ObjectAnimator,它的繼承關係圖如下:
從繼承關係圖中我們可以清晰的看到ValueAnimator和AnimatorSet是繼承與抽象類Animator的,而ObjectAnimator和TimeAnimator是繼承自ValueAnimator的。這個繼承關係圖,大家要好好識記一下,後面會用到,知道這些關係後,我們的討論的方向就更加明確了,總共就這麼幾個類,逐一來看看唄。
在學習ValueAnimator的基本使用之前,先來看下它的一些常用的方法
在上面的方法中,可能大家比較陌生的就是of開頭的那幾個,其中ofFloat,ofInt它們接收的參數類型都是可變參,所以我們可以傳入任何數量的值;傳進去的值列表,就表示動畫時的變化範圍;比如ofInt(3,9,6)就表示從數值3變化到數值9再變化到數值6。而ofObject接收兩個參數一個TypeEvaluator類型的,另一個是Object類型的可變參數,關於TypeEvaluator,後續的文章會有詳細的討論。然後就是ofPropertyValuesHolder多屬性動畫同時工作管理類,有時候我們需要對一個對象的多個屬性做動畫,此時就會用到它。setFrameDelay設置多長時間刷新一幀,默認是10ms。但最終依賴系統的當前狀態,一般我們不用管。
說了這麼多廢話,到底怎麼用,其實ValueAnimator的使用非常簡單,首先來看個最基礎的用法,假如我們想創建一個值從0到1的動畫,動畫的時長為200毫秒,代碼應該這樣寫:
執行上面的代碼就執行了一個值從0到1平滑過渡的動畫,從上面的代碼中可以看出它並沒有與任何的控制項的任何屬性有關係,從它的名字也能看出來它是對值做平滑過渡的,我們怎麼知道呢?很簡單只需要對它做監聽就可以了,我們只需要添加如下代碼:
在上述代碼中我們給valueAnimator添加了一個addUpdateListener監聽,在監聽的回調中,回調給我們的是當前狀態的ValueAnimator 的實例,得到這個實例後通過調用getAnimatedValue就可以拿到當前的值,然後將其列印,這裡有一點需要提醒大家注意的是拿到的這個值的類型是與of..後的值得類型相對應的,ofFloat拿到的就是float類型,ofInt拿到的就是int類型。
運行上述代碼列印結果如下:
從列印結果中可以看到valueAnimator的值在200毫秒內從0逐漸變化到了1,這些中間的過程谷歌已經幫我們實現好了。
在上面我們提到ofFloat(float… values)接收的參數類型是可變參,也就是說這裡傳遞的參數的個數是沒有限制,我們也可以傳遞多個參數,比如
上述代碼就表示在200毫秒內,valueAnimator的值從0變化到3,然後再變化到1。ofInt的使用與ofFloat類似,只不過傳的值的類型不同。
到這裡可能有的同學會問,說了半天沒有看到ValueAnimator做一個動畫啊,那接下來就讓一個View做位移動畫,代碼如下:
在上述代碼中通過對valueAnimator添加監聽,拿到當前幀的值後,不斷的設置ImageView的TranslatonX(該View在X軸的偏移量)值,從而讓其移動。它的運行效果如下:
可以看到我們通過使用ValueAnimator實現了在3秒內在X軸方向上移動100px的效果。這個動畫的操作是在ValueAnimator的監聽中實現的。
小總結: ValueAnimator是計算動畫過程中變化的值,包含動畫的開始值,結束值,持續時間等屬性。但是這些值與我們的控制項是無關的,要想把計算出來的值應用到對象上,必須為ValueAnimator註冊一個監聽器,該監聽器負責更新對象的屬性值。在實現這個監聽器的時候,可以通過getAnimatedValue()的方法來獲取當前動畫的值,拿到這個值後,我們就可以為所欲為了。
4、ObjectAnimator的基本用法相比於ValueAnimator,在開發中可能ObjectAnimator要比ValueAnimator用的多,因為ObjectAnimator可以直接操作對象的屬性,而不用像ValueAnimator那麼麻煩。
假如讓一個ImageView做旋轉的動畫,代碼可以這樣寫:
從上述代碼我們可以看到ObjectAnimator與ValueAnimator的用法有點相似,又有不同,在上述代碼中objectAnimator調用了ofFloat()方法來去創建一個ObjectAnimator的實例,與ValueAnimator不同的是,這裡的ofFloat()方法當中接收的參數有點變化了。這裡第一個參數要求傳入一個object對象,即進行動畫的對象,在上面我們傳了一個ImageView。第二個參數是屬性的名字,因為做旋轉動畫所以這裡傳的屬性的名字為「rotation」。後面就是可變參數了,這裡我們傳的是0,360,表示讓ImageView旋轉360度,然後設置時長,調用start方法。美女效果如下,啊,不是,是運行效果如下:
可以看到美女還是不錯的,啊。。不是,是運行效果還是不錯的。
假如想看到透明度漸變的效果呢,代碼可以這麼寫:
運行效果如下:
在上面的代碼中我們設置裡的「alpha」屬性,讓其在3秒內完成透明度從0到1的變化。
到這裡從總體上看,屬性動畫的用法還是比較簡單的,肯定有的童鞋會有疑問,ofFloat中的第二個參數都是能傳哪些值呢?上面的代碼中傳了個「alpha」和」rotation」,但是究竟它能傳哪些值呢?這一點從其名字中可以看出「屬性」動畫,無疑它是操作對象的屬性的,所以它可以接收任意值,但是這裡有一個前提,那就是這個屬性必須要有get和set方法,什麼意思呢?屬性動畫針對我們傳入的屬性值,比方說「alpha」,它會去尋找這個屬性名所對應的get和set方法,內部會通過java反射機制來調用set函數修改對象屬性值。由此我們可以推斷出ImageView中肯定會有對alpha屬性的get和set操作,通過尋找你會發現這兩個方法在ImageView的父類View中,通過尋找在View中確實找到了這兩個方法如下:
這也進一步驗證我們的說法,到這裡我們也知道,所有繼承自View的控制項都可以進行alpha變換,因為View中就有getAlpha和setAlpha方法。也許到這有的童鞋還會心有餘悸心想上述說的我理解了,但是假如說我想對View的屬性進行變換,不可能每次都要去View的源碼裡去看看它有沒有get和set方法吧,這裡呢,對經常用到的屬性做一個小的總結:
①translationX和translationY:表示在X軸或者Y軸方向上的位移
② scaleX和scaleY:表示在X軸或者Y軸方向上的縮放
③rotation、rotationX和rotationY:這三個屬性控制View對象圍繞支點進行2D和3D旋轉。
④ pivotX和pivotY:這兩個屬性控制著View對象的支點位置,圍繞這個支點進行旋轉和縮放變換處理。默認情況下,該支點的位置就是View對象的中心點。
⑤x和y:這是兩個簡單實用的屬性,它描述了View對象在它的容器中的最終位置,它是最初的左上角坐標和translationX和translationY值的累計和。
⑥ alpha:它表示View對象的alpha透明度。默認值是1(不透明),0代表完全透明(不可見)。
當然我們可以操作的屬性遠遠不止這些,任何屬性只要有get和set方法,我們都可以操作。
ObjectAnimator是屬性動畫框架中最重要的實行類,創建一個ObjectAnimator只需通過他的靜態工廠類直接返回一個ObjectAnimator對象。傳的參數包括一個對象和對象的屬性名字,但這個屬性必須有get和set函數,還包括屬性的初始值,最終值,還可以調用setInterpolator設置曲線函數。
5、Animator監聽對於Animator的監聽在上面的代碼中也略有體現,我們通過調用addUpdateListener這個方法給ValueAnimator添加了一個監聽,其實從ValueAnimator的源碼中可以看出它總共是有兩個監聽器的,監聽器相關源碼:
這裡有一點大家需要明白,大家可以回到開始我們給出的繼承關係圖,從繼承關係圖中我們可以看出
AnimatorSet和ValueAnimator是繼承自Animator的,而ObjectAnimator是繼承自ValueAnimator的。所以對於Animator類中的監聽,AnimatorSet、ValueAnimator、ObjectAnimator都可以用,而ValueAnimator類中的監聽,AnimatorSet中是沒有的,而ObjectAnimator是繼承自ValueAnimator的,所以ValueAnimator和ObjectAnimator都是可以調用的。理論說完,就上實例我們可以這樣為屬性動畫添加AnimatorListener 監聽:
可以看到AnimatorListener提供了對動畫開始、動畫重複、動畫結束、取消動畫做了監聽。但是有時候我們並不需要這麼多啊,比如我們只想監聽動畫的開始,假如用這種方法需要把這四個方法都重寫才行,代碼太冗餘了,谷歌的攻城獅也是想到了這一點,給我們提供了一個適配器AnimatorListenerAdapter,有這個類我們就可以選擇性的,根據需要添加監聽了,比如我們只需要添加動畫開始時的監聽,我們可以這麼做:
有木有比上面簡化了很多,可以看出谷歌對屬性動畫的優化還是下了很多功夫的。
6、組合動畫的實現上面我們都是對一個對象進行單一的動畫,但是一個很酷的動畫往往需要多個動畫協同完成,谷歌也是給我提供了多種實現方式,一起來看看吧。
要想完成多個動畫協同工作需要藉助AnimatorSet這個類,這個類主要提供了三個播放方法,play(),playSequentially(),playTogether()。其中playSequentially()表示多個動畫按順序執,它主要有兩種形式的參數playSequentially(Animator… items)和playSequentially(List <Animator> animator);一個是可變參數,另一個是動畫集合。
playTogether()表示幾個動畫同時執行,它接收的參數類型與playSequentially()一致。最後就是play方法了,play方法接收一個Animator動畫實例,play(Animator anim),調用它之後會返回一個AnimatorSet.Builder的實例,AnimatorSet.Builder中包括以下四個方法
after(Animator anim) 將現有動畫插入到傳入的動畫之後執行
after(long delay) 將現有動畫延遲指定毫秒後執行
before(Animator anim) 將現有動畫插入到傳入的動畫之前執行
with(Animator anim) 將現有動畫和傳入的動畫同時執行
好了,理論完了之後就要聯繫實際了,那接下來我們來做一個這樣的組合效果:讓一張圖片旋轉出廠的同時伴隨著漸變和縮放,代碼可以這樣寫:
運行效果如下
可以看出它是漸變、旋轉、縮放、三種動畫的組合,效果還算不錯。
接著我們來看下play的用法,與上述動畫類似,我們來實現這樣一個動畫,讓一張圖片縮放旋轉出廠,出廠之後讓它消失,可以用play實現,代碼如下:
運行效果如下:
這樣我們就用play實現了一個比較不錯的組合動畫了。
前面我們在學Tween動畫的時候,我們是分兩篇介紹的,一篇是xml文件配置的實現,一篇是代碼的實現,上述我們都是用代碼實現的屬性動畫,那麼怎麼配置xml文件實現的?它的實現也很簡單,首先需要做的就是在res下建立一個animator文件夾,然後創建一個xml文件,/res/animator/roation.xml。
在xml文件中總共有可以用三個標籤,與代碼實現是對應著的
<animator> 對應代碼中的ValueAnimator
<objectAnimator> 對應代碼中的ObjectAnimator
<set< 對應代碼中的AnimatorSet
那麼它們都可以設置哪些屬性值呢?
animator中的屬性如下:
android:duration:表示動畫播放的時長
android:valueFrom:動畫屬性開始的值;取值範圍為float,int和color,如果未指定,動畫開始屬性通過屬性的get方法獲得。顏色用6位16進位數表示(例如:#333333)
android:valueTo:動畫結束值;取值範圍同valueFrom
android:startOffset:取值範圍為int,動畫的start方法被調用後,延遲多少毫秒執行。
android:repeatCount:動畫重複的次數,可以設置為-1或者正整數,-1表示無限循環,假如我們設置成1,<font color=」#FF000000>表示重複執行一次,所以它總共會執行2次。
android:repeatMode:動畫重複模式,取值為repeat和reverse;repeat表示正序重播,reverse表示倒序重播,這與前面講的Tween動畫是類似的。
android:valueType:表示參數值類型,取值為intType和floatType;與android:valueFrom、android:valueTo相對應。如果這裡的取值為intType,那麼android:valueFrom、android:valueTo的值也就要對應的是int類型的數值。float也是一樣。如果如果android:valueFrom、android:valueTo的值設置為color類型的值,則不需要設置這個參數;
android:interpolator:設置加速器;
objectAnimator標籤中的屬性如下:
可以看到與animator中的屬性是差不多的,這裡多了一個
set標籤中的屬性如下:
set標籤只有一個屬性如下:
理論終於說完了,掌握了理論之後,就可以來看妹子了。
最後我們以一個用xml實現的組合動畫結束本篇的內容,我們實現的效果是這樣的,先讓這個妹子進入到屏幕的正中央,然後讓她旋轉360度,再然後讓她離開屏幕,離開屏幕的同時伴隨著透明度的變化。先看效果:
效果還算比較炫酷吧, 這也算是一個稍微複雜一點的動畫了,與之對應的xml配置內容如下:
怎樣將其xml文件加載到程序中呢?代碼也很簡單,只需要這樣寫:
可以看到,直接調用AnimatorInflater的loadAnimator將xml文件加載進來,並給其設置目標對象,最後調用start方法啟動,就完成了。xml文件的配置大家可以根據運行效果自己分析分析。
由於篇幅原因以及今天是陰天的原因,這一篇就寫到這裡了,因為我得趕緊去買個避雷針去。
如果你想看我裝逼,那就鎖定本臺期待接下來的關於屬性動畫的文章吧。
參考文章:https://developer.android.com/guide/topics/resources/animation-resource.html
友情提示:最後完整的代碼請點擊下方的閱讀原文,去原作者的博客獲取,微信對代碼支持不是很好,大量代碼展示不友好,還請去作者博客獲取。