一、屬性動畫簡介
Android 中動畫有很多種,屬性動畫就是其中的一種。所謂的屬性動畫,就是在指定的時間内,通過改變對象的屬性達到變化效果的動畫。在 Android 中,屬性動畫系統是一個強健的架構,幾乎可以為任何内容添加動畫效果。實作屬性動畫也是通過 Android 的屬性動畫系統實作,開發者隻需要定義動畫的一些屬性即可完成,這些屬性如下:
- 時長(duration):指定動畫的時長。預設時長為 300 毫秒。
- 時間插值(interpolator):指定如何根據目前動畫的已播放時長來計算屬性的值。
- 重複計數和行為(repeatCount、repeatMode):指定是否在時長結束後重複播放動畫以及重複播放動畫多少次、循環行為(reverse、restart)。
- Animator 集:将動畫分成由多個動畫集組成,這些動畫集可以一起播放、按順序播放或者在指定的延遲時間後播放。
- 幀重新整理延遲:指定動畫幀的重新整理頻率。預設設定為每 10 毫秒重新整理一次,但應用重新整理幀的速度最終取決于整個系統的繁忙程度以及系統為底層計時器提供服務的速度。
二、屬性動畫的工作原理
2.1 屬性動畫的計算
屬性動畫是通過在指定時間内改變對象的屬性達到變化的動畫,按照一定的幀速,計算出每幀屬性的變化量,實作各種動畫效果(如:勻速動畫、加速動畫效果等)。
上圖描繪了屬性動畫中,主類之間是如何互相協作的。
-
對象跟蹤動畫的時間,例如動畫的已運作時長以及添加了動畫效果的屬性的目前值。ValueAnimator
包含ValueAnimator
和TimeInterpolator
。TypeEvaluator
-
:插值器,用于定義動畫插值,即動畫完成比例與動畫時間比例之間的關系(動畫完成比例跟動畫時間比例不一定一緻)。TimeInterpolator
-
:類型評估程式,用于定義在動畫中如何計算添加了動畫效果的屬性的值,即屬性值跟時間之間的關系(随着時間如何變化)。TypeEvaluator
-
要開始動畫,首先要建立一個
ValueAnimator
對象,并為需要添加動畫效果的屬性設定起始值和結束值,并設定動畫的時長。然後調用
start()
接口,動畫就會播放。
2.2 屬性動畫與視圖動畫的差別
視圖動畫系統僅提供為
View
對象添加動畫效果的功能,是以,如果您想為非
View
對象添加動畫效果,則必須實作自己的代碼才能做到。視圖動畫系統也存在一些限制,它僅能為
View
對象的某些方面添加動畫效果。例如:視圖動畫系統可以為視圖的位置變化、形狀變化添加動畫,但是無法為背景顔色變化添加動畫。
視圖動畫系統的另一個缺點就是它隻會修改視圖繪制位置,而不會修改實際的視圖本身,換句話說,視圖動畫隻是實作一種視圖的動畫效果,視圖本身還是停留在原來的位置。例如:為
Button
對象添加視圖移動的動畫,在按鈕移動動畫播放過程中,按鈕本身的點選事件并不會随着動畫移動,如果要實作點選事件随動畫變化,就需要自己另外實作了。
有了屬性動畫系統,就可以完全擺脫這些束縛,還可以為任何對象(視圖和非視圖)的任何屬性添加動畫效果,并且實際修改的是對象本身。屬性動畫系統在執行動畫方面也更為強健。概括地講,通過屬性動畫系統可以為要添加動畫效果的屬性(例如顔色、位置或大小)配置設定
Animator
,還可以定義動畫的各個方面,例如多個
Animator
的插值和同步。
不過,視圖動畫也是有它的有點,視圖動畫系統的設定需要的時間較短,需要編寫的代碼也較少。在開發過程中,如果視圖動畫可以完成需要執行的所有操作,或者現有代碼已按照您需要的方式運作,則無需使用屬性動畫系統。在某些用例中,也可以針對不同的情況同時使用這兩種動畫系統。
2.3 常用 API 概覽
前面講到了
ValueAnimator
、
TimeInterpolator
和
TypeEvaluator
幾個類,可能大家有點蒙圈,其實在實際使用過程中,有現成的插值器是可以使用的。在 android.animation 包下面可以找到屬性動畫的大多數 API(類),另外,視圖動畫系統在 android.view.animation 包下面定義了許多插值器,這些插值器是可以直接在屬性動畫系統中使用的。
2.3.1 Animator
屬性動畫由抽象類
Animator
提供了建立動畫的基本結構,但是必須通過擴充該類才能實作屬性值動畫,系統 API 已經包含了常用的擴充類供開發者使用。
-
:ValueAnimator
抽象類的直接子類,是屬性動畫的主計時引擎,它也可計算要添加動畫效果的屬性的值。它具有計算動畫值所需的所有核心功能,同時包含每個動畫的計時詳情、有關動畫是否重複播放的資訊、用于接收更新事件的監聽器以及設定待評估自定義類型的功能。為屬性添加動畫效果分為兩個步驟:計算添加動畫效果之後的值,以及對要添加動畫效果的對象和屬性設定這些值。Animator
不會執行第二個步驟,是以,您必須監聽由ValueAnimator
計算的值的更新情況,并使用您自己的邏輯修改要添加動畫效果的對象。ValueAnimator
-
:ObjectAnimator
的子類,用于設定目标對象和對象屬性以添加動畫效果。此類會在計算出動畫的新值後相應地更新屬性。在大多數情況下,直接使用ValueAnimator
可以極大地簡化對目标對象的值添加動畫效果的過程。不過,有時需要直接使用ObjectAnimator
,因為ValueAnimator
存在一些限制,無法達到需要的效果。例如要求目标對象具有特定的通路器方法。ObjectAnimator
-
:這個類提供一種将動畫分組的機制,也就是将多個動畫(或者動畫跟動畫組)組合在一起,以使它們彼此相對運作。通過這個類可以将動畫設定為一起播放、按順序播放或者在指定的延遲時間後播放。AnimatorSet
2.3.2 插值器
前面講到插值器用于定義動畫完成比例與動畫時間比例之間的關系。系統定義了一些插值器,開發者可以直接使用,當然,在必要時可以實作自己的插值器,實作自己的插值器需要繼承
TimeInterpolator
類。系統常用的插值器有以下幾個:
-
:該插值器的變化率在開始和結束時緩慢但在中間會加快。AccelerateDecelerateInterpolator
-
:該插值器的變化率在開始時較為緩慢,然後會加快。AccelerateInterpolator
-
:該插值器先反向變化,然後再急速正向變化。AnticipateInterpolator
-
:該插值器先反向變化,再急速正向變化,然後超過定位值,最後傳回到最終值。AnticipateOvershootInterpolator
-
:該插值器的變化會跳過結尾處。BounceInterpolator
-
:該插值器的動畫會在指定數量的周期内重複。CycleInterpolator
-
:該插值器的變化率開始很快,然後減速。DecelerateInterpolator
-
:該插值器的變化率是恒定不變。LinearInterpolator
-
:該插值器會急速正向變化,再超出最終值,然後傳回。OvershootInterpolator
-
:該接口用于實作您自己的插值器。TimeInterpolator
2.3.3 評估程式
前面提到評估程式是用于定義在動畫中如何計算添加了動畫效果的屬性的值,即屬性值跟時間之間的關系的,根據動畫時間,計算出屬性目前的值。系統定義的評估程式有以下幾種:
-
:這是用于計算IntEvaluator
類型屬性的值的預設評估程式。int
-
:這是用于計算FloatEvaluator
類型屬性的值的預設評估程式。float
-
:這是用于計算顔色屬性的值(用十六進制值表示)的預設評估程式。ArgbEvaluator
-
:此接口用于建立自定義的評估程式。如果要添加動畫效果的對象屬性不是TypeEvaluator
、int
或顔色,就可以自定義的評估程式,自定義評估程式必須實作float
接口,才能計算出對象屬性在添加動畫效果之後的值。TypeEvaluator
三、使用入門
3.1 使用 ValueAnimator 添加動畫效果
借助
ValueAnimato
r 類,可以為動畫播放期間某些類型的值添加動畫效果,隻需指定一組要添加動畫效果的
int
、
float
或顔色值即可。
可以通過調用
ValueAnimator
的任一工廠方法來擷取
ValueAnimator
執行個體對象:
ofInt()
、
ofFloat()
或
ofObject()
。然後設定相應的動畫參數(時長、重複模式、重複次數等等),最後調用
start()
方法開始動畫。示例:
ValueAnimator.ofFloat(1.0f, 2.0f).apply {
duration = 1000 // 動畫時長
repeatMode = ValueAnimator.REVERSE // 重複模式(重新開始)
repeatCount = ValueAnimator.INFINITE // 重複次數(無限循環)
start()
}
注意事項:前面提到,為屬性添加動畫效果分為兩個步驟,計算屬性值和設定對象的屬性值。
ValueAnimator
計算出屬性值,但是不會執行第二步(設定對象屬性值) ,是以,必須添加
ValueAnimator
監聽由
AnimatorUpdateListener
計算的值的更新情況,并将新值設定到對象的屬性中。
ValueAnimator
通過添加
AnimatorUpdateListener
監聽計算值的更新,在
onAnimationUpdate()
将
ValueAnimator
計算的值,設定到對象的相應屬性中,讓動畫展現出來。
ValueAnimator.ofFloat(1.0f, 2.0f).apply {
duration = 1000 // 動畫時長
repeatMode = ValueAnimator.REVERSE // 重複模式(重新開始)
repeatCount = ValueAnimator.INFINITE // 重複次數(無限循環)
addUpdateListener { // 添加 AnimatorUpdateListener 監聽
// ValueAnimator 無法自動将更新的屬性值設定到對象的屬性中,隻能在監聽中實作對象屬性值的設定。
// 監聽到計算值的更新,并設定到對象屬性中
val value = it.animatedValue as Float
imageView.scaleX = value
imageView.scaleY = value
}
start()
}
- 實作效果
溫馨提示:的工廠方法(
ValueAnimator
、
ofInt()
或
ofFloat()
)是支援傳入多個值的,至少有兩個值才會有動畫效果。
ofObject()
有自己預設的插值器,預設的插值器是
ValueAnimator
。
AccelerateDecelerateInterpolator
3.2 使用 ObjectAnimator 添加動畫效果
在前面已經提到
ObjectAnimator
是
ValueAnimator
的子類,它繼承了父類的動畫引擎和值計算等,靈活性雖然不及
ValueAnimator
,但是
ObjectAnimator
支援通過屬性名稱為目标對象屬性添加動畫效果,這可以極大地簡化為對象添加動畫效果的過程,而且動畫屬性會自動更新,無需再實作
ValueAnimator.AnimatorUpdateListener
。
執行個體化
ObjectAnimator
與
ValueAnimator
的過程類似,但執行個體化
ObjectAnimator
對象時可以傳入該對象需要添加動畫的屬性的名稱(以字元串形式),以及實作動畫效果的屬性值(清單)。
- 示例代碼
ObjectAnimator.ofFloat(imageView, "scaleY", 1.0f, 2.0f).apply {
repeatMode = ValueAnimator.REVERSE
repeatCount = ValueAnimator.INFINITE
duration = 1000
start()
}
第一個參數是目标對象,第二個參數是屬性名稱,第三個參數是個數組,是屬性值清單。
- 實作效果 要使
正确更新屬性實作動畫效果,必須執行以下操作:ObjectAnimator
- 傳入的屬性名稱(即要添加動畫效果的對象屬性)必須具有
形式的 setter 函數(采用駝峰式大小寫形式)。由于set<PropertyName>()
會在動畫過程中自動更新屬性,是以必須能夠使用此 setter 方法通路該屬性。例如,上面提到的ObjectAnimator
,則需要存在scaleX
方法,并且有權限調用。如果此 setter 方法不存在,有三個選擇可以處理:setScaleX()
- 如果有權限修改類,可将 setter 方法添加到類中。
- 使用有權限更改的封裝容器類,并在該封裝容器使用有效的 setter 方法接收值并将其轉發給原始對象。
- 改用
。ValueAnimator
- 如果在
的工廠方法中,僅指定一個值,則系統會假定它是動畫的結束值。是以,要添加動畫效果的對象屬性必須具有用于擷取屬性起始值的 getter 函數。getter 函數必須采用ObjectAnimator
形式。例如,上面提到的get<PropertyName>()
必須要有scaleX
方法。getScaleX()
- 要添加動畫效果的屬性的 getter(如果需要)和 setter 方法的操作對象必須與
指定的起始值和結束值的類型相同。ObjectAnimator
- 根據要添加動畫效果的屬性或對象,可能需要對視圖調用
方法,以強制螢幕使用添加動畫效果之後的值重新繪制自身。可以在invalidate()
回調中執行此操作(視圖的所有屬性 setter(如 setAlpha() 和 setScaleX())都會使視圖失效,是以,在使用新值調用這些方法時,您無需使視圖失效)。onAnimationUpdate()
3.3 使用 AnimatorSet 編排多個動畫
前面介紹的屬性動畫是單個動畫(當然,你可以使用
ValueAnimator
同時為多個屬性設定動畫效果,但這個局限于同時執行),通過
AnimatorSet
可以将多個動畫編排在一起(也可以嵌套
AnimatorSet
),并可控制不同的動畫之間的播放關系(之前播放、一起播放、之後播放等)。
AnimatorSet
建構完成之後,調用
start()
啟動動畫。
- 示例
// 定義X方向縮放動畫
val scaleXAnim = ObjectAnimator.ofFloat(imageView, "scaleY", 1.0f, 2.0f).apply {
duration = 1000
}
// 定義Y方向縮放動畫
val scaleYAnim = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0f, 2.0f).apply {
duration = 1000
}
// 定義縮放AnimatorSet
val scaleAnimSet = AnimatorSet().apply {
// 同時播放X縮放動畫和Y縮放動畫
play(scaleXAnim).with(scaleYAnim)
}
// 定義透明度動畫
val alphaAnim = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.0f).apply {
duration = 1000
}
// 定義AnimatorSet
AnimatorSet().apply {
// 先播放縮放AnimatorSet,接着播放透明度動畫
play(scaleAnimSet).before(alphaAnim)
// 啟動AnimatorSet
start()
}
- 效果
注意事項:
1. 組合成
的
AnimatorSet
在定義是不要調用
Animator
start()
方法;
2.
開始播放動畫需要調用
AnimatorSet
start()
方法;
3.
AnimatorSet
3.4 動畫監聽
屬性動畫監聽有兩個,分别是
Animator.AnimatorListener
和
ValueAnimator.AnimatorUpdateListener
,都是用來監聽動畫播放期間的重要事件。
-
:監聽動畫的各個狀态,回調方法有:Animator.AnimatorListener
-
- 在動畫開始播放時回調。onAnimationStart()
-
- 在動畫結束播放時回調,無論何種方式結束動畫都會調用。onAnimationEnd()
-
- 在動畫重複播放時回調。onAnimationRepeat()
-
- 在動畫取消播放時回調,取消的動畫也會調用onAnimationCancel()
。onAnimationEnd()
-
-
:監聽動畫的更新事件,回調方法有:ValueAnimator.AnimatorUpdateListener
-
- 在動畫的每一幀回調。使用此監聽可以在動畫播放過程中實時擷取生成的計算值,通過回調方法參數onAnimationUpdate()
對象的ValueAnimator
方法可以擷取目前計算值,通過此計算值可以更新動畫效果。如果使用getAnimatedValue()
實作屬性動畫,則必須通過此監聽來更新動畫(ValueAnimator
不會自動更新動畫)。在此回調方法中更改視圖的動畫效果,可能需要對視圖調用ValueAnimator
方法更新視圖。如果調用視圖的方法進行更新動畫,并且改方法包含了invalidate()
,則無需再調用invalidate()
,例如:視圖的invalidate()
、setScaleX()
等方法。setAlpha()
-
如果不需要實作接口的所有方法,則可以擴充
Animator.AnimatorListener
類,
AnimatorListenerAdapter
類提供了方法的空實作。
AnimatorListenerAdapter
3.5 在 XML 資源中定義屬性動畫
Android 提供了強大的 XML 資源定義,也包括了屬性動畫。屬性動畫 XML 資源放在
res/animator
目錄下。
3.5.1 XML資源定義 ValueAnimator
ValueAnimator
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:valueType="floatType"
android:valueFrom="1.0"
android:valueTo="2.0">
</animator>
在代碼中使用
AnimatorInflater.loadAnimator()
方法加載動畫,并調用
start()
方法播放動畫。
(AnimatorInflater.loadAnimator(this@LayoutAnimActivity, R.animator.animator_scale) as ValueAnimator).apply {
addUpdateListener {
// ValueAnimator 無法自動将更新的屬性值設定到對象的屬性中,隻能在監聽中實作對象屬性值的設定。
val value = it.animatedValue as Float
imageView.scaleX = value
imageView.scaleY = value
}
start()
}
說明:因為不會自動更新視圖屬性,是以,在 XML 定義
ValueAnimator
時,在代碼中加載動畫時,仍需要添加
ValueAnimator
并在回調方法中更新視圖屬性,實作動畫效果。
AnimatorUpdateListner
在代碼中定義
ValueAnimator
時,可以指定多個值,但在 XML 中隻能定義
valueFrom
和
valueTo
兩個值。不過在 API 23 開始,XML 定義動畫增加了關鍵幀的概念,而且每個關鍵幀(使用
propertyValuesHolder
和
keyframe
标簽定義)還可以定義不同的插值器。
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="infinite">
<propertyValuesHolder>
<keyframe android:valueType="floatType" android:fraction="0" android:value="1.0" />
<keyframe android:valueType="floatType" android:fraction="0.5" android:value="1.2" />
<keyframe android:valueType="floatType" android:fraction="1" android:value="2.0" />
</propertyValuesHolder>
</animator>
添加關鍵幀用标簽封裝,每一個關鍵幀用
propertyValuesHolder
标簽定義。
keyframe
是關鍵幀在動畫過程中的位置(占比分數)。
android:fraction
3.5.2 XML 資源定義 ObjectAnimator
ObjectAnimator
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:valueType="floatType"
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha">
</objectAnimator>
在代碼中使用
AnimatorInflater.loadAnimator()
方法加載動畫,設定目标,并調用
start()
方法播放動畫。
(AnimatorInflater.loadAnimator(this@LayoutAnimActivity, R.animator.animator_alpha) as ObjectAnimator).apply {
target = imageView // ObjectAnimator 需要設定目标
start()
}
在 API 23 開始,同樣可以使用關鍵幀(使用
propertyValuesHolder
和
keyframe
标簽定義),用來定義更加複雜的動畫,而且每個關鍵幀還可以定義不同的插值器。
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="infinite">
<propertyValuesHolder android:propertyName="alpha" android:valueType="floatType">
<keyframe android:valueType="floatType" android:fraction="0.0" android:value="1.0" />
<keyframe android:valueType="floatType" android:fraction="0.5" android:value="0.2" />
<keyframe android:valueType="floatType" android:fraction="1.0" android:value="0.0" />
</propertyValuesHolder>
</objectAnimator>
注意事項:
1. 在 XML 中定義
的時候,必須指定添加動畫的屬性,在代碼中加載動畫之後,也需要設定目标,并調用
ObjectAnimator
start()
才會啟動動畫;
2. 如果使用了
,必須在
propertyValuesHolder
propertyValuesHolder
标簽内指定添加動畫的屬性名稱;
3. 通過
可以實作同時為多個屬性添加動畫(類似
propertyValuesHolder
的效果),但是每個屬性需要定義一個
AnimatorSet
标簽(隻有在 API 23 開始才支援)。
propertyValuesHolder
在
ObjectAnimator
使用
propertyValuesHolder
可以實作同時為多個屬性添加動畫效果
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="infinite">
<propertyValuesHolder android:propertyName="alpha" android:valueType="floatType" android:valueFrom="1.0" android:valueTo="0.0" />
<propertyValuesHolder android:propertyName="scaleX" android:valueType="floatType" android:valueFrom="1.0" android:valueTo="2.0" />
<propertyValuesHolder android:propertyName="scaleY" android:valueType="floatType" android:valueFrom="1.0" android:valueTo="2.0" />
</objectAnimator>
3.5.3 XML 資源定義 AnimatorSet
AnimatorSet
在
ObjectAnimator
使用
propertyValuesHolder
同時為多個屬性添加動畫效果,僅API 23開始,低于使用
AnimatorSet
實作,一下示例跟上面有同樣效果
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<set>
<objectAnimator
android:duration="500"
android:propertyName="scaleX"
android:valueTo="1.5"
android:valueType="floatType"
android:repeatCount="infinite"
android:repeatMode="reverse"/>
<objectAnimator
android:duration="500"
android:propertyName="scaleY"
android:valueTo="1.5"
android:valueType="floatType"
android:repeatCount="infinite"
android:repeatMode="reverse" />
</set>
<objectAnimator android:propertyName="alpha"
android:duration="500"
android:valueFrom="1.0"
android:valueTo="0.5"
android:valueType="floatType"
android:repeatCount="infinite"
android:repeatMode="reverse" />
</set>
四、編後語
Android 的屬性動畫非常強大,應用适當的話可以節省很多開發時間,本篇文章先介紹入門基礎,後續會發表一些關于屬性動畫的使用技巧。
下一篇:Android 屬性動畫(二)為視圖狀态更改添加動畫效果