響應絲滑動畫一篇文章中,分別介紹了作用於普通檢視、繪製檢視的繪製物件、和介面這三種物件的動畫效果,但是都有一些使用的侷限性。比如這些動畫都只是以螢幕上繪製更新的方式繪製動畫,並沒有真實改變作用物件的實際位置或屬性,這種問題在檢視動畫中尤為明顯,在沒有特別設定時,動畫結束後的檢視狀態會還原到動畫前,也就是說動畫中及動畫後的檢視物件是沒有儲存動畫中用到的一些屬性的。這種情況就需要使用本文所介紹的屬性動畫了。

屬性動畫本質是對某個物件的屬性提供一組變化更新的屬性值,他的作用物件不僅可以是檢視和介面,也可以是任何具有上述需求的物件類。在Android3.0 API 11開始的AndroidSDK版本中,定義了android.animation.Animator類作為抽象父類來描述屬性動畫的結構,並提供了android.animation.ValueAnimator作為只變化屬性值的屬性動畫類、android.animation.ObjectAnimator可以變化屬性值並更新到物件屬性的屬性動畫類、和android.animation.AnimatorSet包含多個屬性動畫集合的屬性動畫類。

與普通動畫類似,屬性動畫同樣可以在資原始檔中靜態宣告,也可以在程式碼中動態宣告,但是由於其作用物件可以是任何物件,而且屬性動畫的優勢在於為作用物件的屬性提供一組變化的屬性值,所以屬性動畫只能在需要繫結作用物件的程式碼位置動態使用。由於靜態宣告的屬性動畫可以針對多個物件使用,在開發過程中推薦使用靜態宣告+動態使用的方式。

只變化屬性值的屬性動畫類

ValueAnimator作為最核心的屬性動畫類,可以呼叫ValueAnimator.ofArgb(int... values)ValueAnimator.ofFloat(float... values)ValueAnimator.ofInt(int... values)ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)系列靜態方法,獲取初始化後的例項化物件。其中的引數 values 是可變長引數,作為可變化屬性值的範圍區間。而引數 evaluatorandroid.animation.TypeEvaluator估值器介面的實現類,用以計算估計屬性值的具體變化數值的類。而AndroidSDK中已經定義了上述系列靜態方法前三種類型對應的估值器子類,分別是android.animation.ArgbEvaluatorandroid.animation.FloatEvaluatorandroid.animation.IntEvaluator,除此之外還有計算二維座標系的估值器android.animation.PointFEvaluator和計算矩形座標的估值器android.animation.RectEvaluator。另外,在實際開發中針對作用物件要變化的屬性值型別不同,也可以自定義實現TypeEvaluator介面的估值器。

這裡提到的ArgbEvaluator是用以計算顏色的 ARGB 色值估值器,與Android系統定義的螢幕座標類似,這裡的 ARGB 色值也是一套系統顏色規範。在Java中int型別是用4個位元組儲存的,這4個位元組按順序從大端到小端,剛好分別表示Alpha(顏色值中的透明度),Red(顏色值中的紅色值),Green(顏色值中的綠色值),Blue(顏色值中的藍色值),每個位元組的數值可以表示範圍是[ 0x0, 0xFF ],所以用十六進位制表示顏色值中的純紅色為 0xFFFF0000 ,純黑色為 0xFF000000 ,純白色為 0xFFFFFFFF ,而完全透明色為 0x000000000x00FFFFFF 之間的任一值。也可以藉助android.graphics.Color類使用已經定義的顏色值和系統顏色規範的相關方法。

在建立ValueAnimator例項化物件後,

可以呼叫setDuration(long duration)設定完成一次動畫效果的持續時間,單位為 ms 毫秒。

呼叫setRepeatCount(int value)設定完成一次動畫效果後的重複次數,預設 value = ValueAnimator.INFINITE 為無限迴圈。

呼叫setRepeatMode(int value)設定在完成一次動畫效果後重復時的動畫效果,其 value 只能為

ValueAnimator.RESTART 表示從頭開始重新完成一次一模一樣的動畫效果,

ValueAnimator.REVERSE 表示從上次動畫結尾開始完成一次倒放的動畫效果。

呼叫 setInterpolator(TimeInterpolator value)設定動畫效果的時間插值器。與上面的估值器定義結構類似,引數 valueandroid.animation.TimeInterpolator插值器介面的實現類,用以確定每次更新動畫效果的時間。同樣的,AndroidSDK已經定義了一系列插值器,包括但不限於持續加速插值器android.view.animation.AccelerateInterpolator、持續減速插值器android.view.animation.DecelerateInterpolator、線性勻速插值器android.view.animation.LinearInterpolator等,同樣在實際開發中針對動畫效果展示時間進度,也可以自定義實現TimeInterpolator介面的插值器。

最終在需要啟動當前屬性動畫的位置呼叫start()即開始播放當前動畫效果,如果不想在呼叫屬性動畫的啟動方法後立即執行相關動畫效果,可以在啟動方法之前呼叫setStartDelay(long startDelay)設定延時動畫的延長時間,單位是 ms 毫秒。

ValueAnimator是根據TypeEvaluator估值器來更新屬性值的,而更新的時間則是由TimeInterpolator插值器決定的,那麼怎麼才能拿到每次更新後的屬性值呢?這裡就可以用呼叫ValueAnimator物件的addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)方法新增屬性動畫更新監聽,在AnimatorUpdateListener類中重寫onAnimationUpdate(ValueAnimator animation)方法,該方法中的引數animation即更新後的屬性動畫物件,通過呼叫animation.getAnimatedValue()來接收每次更新後的屬性值,並可以將更新後的屬性值賦值給需要屬性動畫的物件中。

也可以在資原始檔中靜態定義屬性動畫,在 res/animator 目錄下,定義xml格式的屬性動畫資原始檔,該資原始檔內以<animator />為根標籤,其中可以設定android:duration作為完成一次動畫效果的持續時間等一系列屬性,與程式碼中動態設定的相關方法類似。之後在程式碼使用位置載入該資原始檔,通過呼叫AnimatorInflater.loadAnimator(Context context, int id)的靜態方法,引數context作為呼叫位置所在的上下文環境,引數id則是要載入的屬性動畫型別的資原始檔名。將得到Animator抽象類的返回結果,根據所載入資原始檔中的根標籤名判斷,如果與<animator />一致,則可將返回的Animator抽象類直接轉換為ValueAnimator型別的物件。

可變化屬性值並更新到物件屬性的屬性動畫類

ObjectAnimator作為ValueAnimator的子類,定義和使用方式都有類似的地方,只是ObjectAnimator類中封裝了屬性動畫的更新監聽方法,因此只要繫結物件及其要變化的屬性,在屬性動畫每次變化屬性值時,都會主動將屬性值更新到繫結物件的相關屬性上。

因此在初始化例項物件時需要呼叫ofArgb(Object target, String propertyName, int... values)ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)ofInt(T target, Property<T, Integer> property, int... values)ofObject(T target, Property<T, V> property, TypeConverter<PointF, V> converter, Path path)等系列靜態方法。

其中引數 target 即為要繫結的動畫效果作用物件;

引數 propertyNametarget 物件中的某個屬性,而且該屬性必須要有符合駝峰命名規則的 getset 方法;

引數 values 仍然作為可變化屬性值的範圍區間;

引數 path 作為android.graphics.Path類,則表示一段動畫效果的執行路徑,以此替換引數 values 表示的單一變化區間;

引數 property 作為android.util.Property抽象類,同樣描述某種屬性型別,以此替換String型別的引數 propertyName

引數 converter 作為android.animation.TypeConverter抽象類,與上文的引數 property 同時使用,當 property 中宣告的屬性型別與實際變化更新的屬性值不一致時,使用引數 converter 所表示的強制型別轉換方式。

在建立例項化物件後,ObjectAnimator有與ValueAnimator一致的setXXX()getXXX()系列方法,另外還有setTarget(Object target)方法可以直接繫結要變化屬性值的目標物件,同時有setPropertyName(String propertyName)setProperty(Property property)兩種呼叫方法,都能實現對目標物件中的目標屬性的繫結。

最後仍然是在需要啟動當前屬性動畫的位置呼叫start()開始播放當前動畫效果。

在資原始檔中靜態定義時,同樣在 res/animator 目錄下,定義xml格式的屬性動畫資原始檔,不過該資原始檔內根標籤為<objectAnimator />以標記關聯物件的屬性動畫,其中的屬性設定不僅與只變化屬性值的ValueAnimator對應的<animator />標籤中的屬性一致,還可單獨設定android:propertyName繫結屬性名稱。之後同樣可以在程式碼中呼叫AnimatorInflater.loadAnimator(Context context, int id)靜態方法載入當前屬性動畫資原始檔,同樣地,針對得到的Animator抽象類的返回結果,根據所載入資原始檔中的根標籤名判斷,如果與<objectAnimator />一致,則可將返回的Animator抽象類直接轉換為ObjectAnimator型別的物件。

包含多個屬性動畫集合的屬性動畫類

SetAnimator是將一系列上述單獨的屬性動畫組合起來的屬性動畫合集,其使用目的主要是為了講不同的屬性動畫按照同一條時間線整理播放,因此其相關方法主要與其中子動畫的播放順序相關。

通過直接建立SetAnimator()構造方法可以獲得SetAnimator例項化物件。

得到的物件呼叫playTogether(Animator... items)方法可以在同一段時間內播放新增的子動畫,

呼叫playSequentially(Animator... items)方法可以將其中的子動畫按照新增順序播放,

而這兩個系列方法中的可變長引數 items 即是要新增的系列子動畫物件。

也可以將得到的物件呼叫play(Animator anim)方法,引數 anim 為新增的基本動畫,返回android.animation.AnimatorSet.Builder型別,可以呼叫該類的before(Animator anim)方法設定針對基本動畫之前播放的動畫 anim 、呼叫該類的with(Animator anim)方法設定與基本動畫同時播放的動畫 anim 、呼叫該類的after(Animator anim)方法設定針對基本動畫之後播放的動畫 anim 、以及該類的after(long delay)方法設定在基本動畫之後延時 delay 毫秒之後繼續播放之後動畫。

最後仍然是在需要啟動當前屬性動畫的位置呼叫start()開始播放當前動畫效果。

在資原始檔中靜態定義時,同樣在 res/animator 目錄下,定義xml格式的屬性動畫資原始檔,在資原始檔內根標籤為<set></set>以標記為集合屬性動畫,該標籤內可以設定屬性android:ordering,其值只能為預設的together表示子動畫同時執行,或者為sequentially表示子動畫按順序執行。在<set></set>標籤內部,可以嵌入上述<animator /><objectAnimator />兩種屬性動畫標籤。同樣可以在程式碼中呼叫AnimatorInflater.loadAnimator(Context context, int id)靜態方法載入當前屬性動畫資原始檔,同樣地,針對得到的Animator抽象類的返回結果,根據所載入資原始檔中的根標籤名判斷,如果與<set></set>一致,則可將返回的Animator抽象類直接轉換為AnimatorSet型別的物件。