一. 簡(jiǎn)介
屬性動(dòng)畫(Property Animation)是Android 3.0(API 11)之后提供的動(dòng)畫框架,在之前Android 提供了 幀動(dòng)畫(Frame Animation) 和 補(bǔ)間動(dòng)畫(Tween Animation),幀動(dòng)畫就是把一個(gè)動(dòng)畫分成多張圖片,然后把這些圖片連貫起來(lái)播放,原理和動(dòng)畫片類似;補(bǔ)間動(dòng)畫是針對(duì)View 進(jìn)行的平移、旋轉(zhuǎn)、縮放、透明度變化 等動(dòng)畫效果;屬性動(dòng)畫則是通過(guò)改變屬性值來(lái)實(shí)現(xiàn)動(dòng)畫效果,不再局限于View。
屬性動(dòng)畫和補(bǔ)間動(dòng)畫的對(duì)比:
作用對(duì)象
補(bǔ)間動(dòng)畫的作用對(duì)象僅為View,而屬性動(dòng)畫則可作用于非View 對(duì)象。有些情況下,可能需要對(duì)View 的某個(gè)對(duì)象進(jìn)行操作,而不是整個(gè)View 對(duì)象,比如動(dòng)態(tài)改變View 的顏色,此時(shí)補(bǔ)間動(dòng)畫便無(wú)法實(shí)現(xiàn)。對(duì)View 的影響
補(bǔ)間動(dòng)畫只改變View 的視覺(jué)效果,并不改變View 的屬性,比如將一個(gè)Button 從屏幕左上角 移動(dòng)至 右下角,使用補(bǔ)間動(dòng)畫的話,移動(dòng)完成之后 Button 的點(diǎn)擊區(qū)域還是左上角,因?yàn)樗奈恢脤傩圆](méi)有改變。而屬性動(dòng)畫,顧名思義,改變的就是屬性。動(dòng)畫效果
補(bǔ)間動(dòng)畫效果比較單一,僅支持平移、旋轉(zhuǎn)、縮放、透明度變化,而屬性動(dòng)畫比較靈活,可以實(shí)現(xiàn)多種復(fù)雜的動(dòng)畫效果。
在現(xiàn)在的開(kāi)發(fā)過(guò)程中,由于屬性動(dòng)畫已經(jīng)可以實(shí)現(xiàn)幾乎所有補(bǔ)間動(dòng)畫能實(shí)現(xiàn)的功能,所以我們最常用的還是屬性動(dòng)畫,本篇將簡(jiǎn)介屬性動(dòng)畫的基本使用。
二. 工作原理及相關(guān)類
1. 屬性動(dòng)畫的工作過(guò)程

從上面的工作過(guò)程可以看到,屬性動(dòng)畫框架中最常用也是最重要的幾個(gè)類,就是ValueAnimator、ObjectAnimator、Interpolator 的實(shí)現(xiàn)類 和 TypeEvaluator 的實(shí)現(xiàn)類。
2. 相關(guān)類簡(jiǎn)介
Animators:
1)ValueAnimator,屬性動(dòng)畫的主要計(jì)時(shí)引擎,它還計(jì)算要設(shè)置動(dòng)畫的屬性的值。它具有計(jì)算動(dòng)畫值的所有核心功能,包含每個(gè)動(dòng)畫的持續(xù)時(shí)間,有關(guān)動(dòng)畫是否重復(fù)進(jìn)行,更新屬性值的事件偵聽(tīng)器以及設(shè)置自定義類型的估值器功能。屬性動(dòng)畫有兩個(gè)部分:計(jì)算動(dòng)畫過(guò)程中的屬性值,給對(duì)象和屬性上設(shè)置這些值。ValueAnimator 僅負(fù)責(zé)計(jì)算值,因此必須監(jiān)聽(tīng)ValueAnimator 計(jì)算的值的更新,并使用按照自己的邏輯修改要設(shè)置動(dòng)畫的對(duì)象。
主要方法:
| 方法簽名 | 用途 |
|---|---|
| public static ValueAnimator ofInt (int... values) | ofInt() 作用有兩個(gè): 1. 創(chuàng)建動(dòng)畫實(shí)例 2. 將傳入的多個(gè)Int參數(shù)進(jìn)行平滑過(guò)渡: 如果傳入0和1,表示將值從0平滑過(guò)渡到1,如果傳入了3個(gè)Int參數(shù) a,b,c ,則是先從a平滑過(guò)渡到b,再?gòu)腷平滑過(guò)渡到C,以此類推。 ValueAnimator.ofInt() 內(nèi)置了整型估值器,直接采用默認(rèn)的,不需要設(shè)置,即默認(rèn)設(shè)置了如何從初始值 過(guò)渡到 結(jié)束值。 |
| public static ValueAnimator ofFloat (float... values) | 類似ofInt |
| public static ValueAnimator ofArgb (int... values) | api 21 時(shí)加入,傳入的參數(shù)是顏色的int 值,提供顏色值的平滑變換,和ofInt 的區(qū)別在于使用的估值器不同 |
| public static ValueAnimator ofObject (TypeEvaluator evaluator, Object... values) | 可傳入對(duì)象作為參數(shù)創(chuàng)建Animator 實(shí)例,由于可以傳任意對(duì)象,所以需要自己提供估值器 |
| public ValueAnimator setDuration (long duration) | 設(shè)置動(dòng)畫持續(xù)時(shí)長(zhǎng),單位毫秒 |
| public void setRepeatCount (int value) | 設(shè)置動(dòng)畫重復(fù)次數(shù) |
| public void setRepeatMode (int value) | 設(shè)置動(dòng)畫重復(fù)模式,有兩種取值:RESTART 或 REVERSE,顧名思義就是重新開(kāi)始 和 倒著執(zhí)行 |
| public void setStartDelay (long startDelay) | 設(shè)置延遲多長(zhǎng)時(shí)間開(kāi)始動(dòng)畫,單位毫秒 |
| public boolean isRunning () | 判斷動(dòng)畫是否正在運(yùn)行 |
| public void addUpdateListener (ValueAnimator.AnimatorUpdateListener listener) | 添加值變化時(shí)的監(jiān)聽(tīng)事件,若要使用ValueAnimator 實(shí)現(xiàn)動(dòng)畫就必須要添加一個(gè)Listener,在數(shù)據(jù)變化之后改變對(duì)象屬性來(lái)實(shí)現(xiàn)動(dòng)畫 |
| public void start () | 開(kāi)始動(dòng)畫,如果沒(méi)有設(shè)置延時(shí)開(kāi)始,動(dòng)畫會(huì)立即開(kāi)始,設(shè)置了則會(huì)延時(shí)一段時(shí)間后開(kāi)始 |
| public void cancel () / public void end () | 取消動(dòng)畫 / 結(jié)束動(dòng)畫,兩者的區(qū)別就是調(diào)用cancel 會(huì)停止在當(dāng)前狀態(tài),end 會(huì)將屬性值設(shè)置為結(jié)束值 |
| public void addListener (Animator.AnimatorListener listener) | 是父類Animator 的方法,添加Listener,監(jiān)聽(tīng)動(dòng)畫開(kāi)始、結(jié)束、取消、重復(fù)等事件 |
| public void pause () / public void resume () | 暫停 / 重新開(kāi)始動(dòng)畫,調(diào)用這兩個(gè)方法的線程必須和動(dòng)畫開(kāi)始的線程相同,另外,如果對(duì)一個(gè)沒(méi)有暫停的動(dòng)畫調(diào)用resume 方法將會(huì)被忽略 |
以上,只是比較常用的方法,還有許多諸如getter 等方法,這里不一一列舉,詳情可參官方文檔。
2)ObjectAnimator,是ValueAnimator的子類,允許我們將目標(biāo)對(duì)象和對(duì)象屬性設(shè)置為動(dòng)畫,它在計(jì)算動(dòng)畫的新值時(shí)會(huì)相應(yīng)地更新屬性,我們?cè)诖蠖鄶?shù)情況下可以使用ObjectAnimator,因?yàn)樗沟迷谀繕?biāo)對(duì)象上設(shè)置動(dòng)畫值的過(guò)程變得更加容易。但是,ObjectAnimator還有一些限制,例如要求在目標(biāo)對(duì)象上存在特定的acessor方法。
主要方法和ValueAnimator 類似,只是創(chuàng)建對(duì)象的ofXXX 方法有些不同,常用如下
| 方法簽名 | 用途 |
|---|---|
| public static ObjectAnimator ofArgb (Object target, String propertyName, int... values) | 構(gòu)造并返回一個(gè)在顏色值之間設(shè)置動(dòng)畫的ObjectAnimator,values 傳單個(gè)值意味著該值是動(dòng)畫的結(jié)束值,在這種情況下,起始值將從正在執(zhí)行動(dòng)畫的屬性的值和第一次調(diào)用start() 時(shí)的目標(biāo)對(duì)象產(chǎn)生。兩個(gè)值的話意味著起始值和結(jié)束值。兩個(gè)以上的值意味著起始值,沿途的動(dòng)畫值和結(jié)束值 |
| public static ObjectAnimator ofFloat (Object target, String xPropertyName, String yPropertyName, Path path) | 使用兩個(gè)屬性沿Path設(shè)置動(dòng)畫,“路徑”動(dòng)畫以二維方式移動(dòng),將坐標(biāo)(x,y)設(shè)置為動(dòng)畫,以跟隨線條。坐標(biāo)是浮點(diǎn)數(shù),它們被分別設(shè)置到由xPropertyName和yPropertyName指定的屬性。 |
| public static ObjectAnimator ofFloat (Object target, String propertyName, float... values) | 為指定對(duì)象target 的指定屬性propertyName 設(shè)置值 |
| public static ObjectAnimator ofInt (T target, Property<T, Integer> xProperty, Property<T, Integer> yProperty, Path path) | 和上面提到的ofFloat 類似,只不過(guò)參數(shù)值的類型是int 值 |
需要注意的是,在給對(duì)象的指定屬性設(shè)置值時(shí),要求對(duì)象內(nèi)必須有屬性對(duì)應(yīng)的getter 和setter。更多方法可以查文檔。
3)AnimatorSet
提供一種將動(dòng)畫分組在一起的機(jī)制,以便它們相互運(yùn)行,可以將動(dòng)畫設(shè)置為一起播放,按順序播放,或在指定的延遲后播放。主要方法如下
| 方法簽名 | 用途 |
|---|---|
| public AnimatorSet.Builder play (Animator anim) | 此方法創(chuàng)建一個(gè)Builder對(duì)象,用于設(shè)置動(dòng)畫之間的播放約束。這個(gè)初始的play() 方法告訴Builder動(dòng)畫,它是對(duì)Builder的后續(xù)命令的依賴。例如,調(diào)用play(a1).with(a2) 將AnimatorSet 設(shè)置為同時(shí)播放a1和a2,play(a1).before(a2) 設(shè)置AnimatorSet首先播放a1,然后播放a2,而 play(a1).after(a2) 將AnimatorSet 設(shè)置為先播放a2,然后播放a1。請(qǐng)注意,play() 是告訴Builder 創(chuàng)建依賴關(guān)系的動(dòng)畫的唯一方法,因此對(duì)Builder 中各種函數(shù)的連續(xù)調(diào)用都將引用play() 中提供的初始參數(shù)作為其他動(dòng)畫的依賴關(guān)系。例如,當(dāng)a1結(jié)束時(shí),調(diào)用play(a1).before(a2).before(a3) 將同時(shí)播放a2和a3; 它沒(méi)有在a2 和a3 之間建立依賴關(guān)系。 |
| public void playSequentially (List<Animator> items) | 順序執(zhí)行動(dòng)畫列表 |
| public void playTogether (Collection<Animator> items) | 同時(shí)播放集合中的動(dòng)畫 |
| public void setCurrentPlayTime (long playTime) | 設(shè)置時(shí)間進(jìn)度到指定的時(shí)間點(diǎn),值應(yīng)該在0 和 動(dòng)畫集結(jié)束的總時(shí)長(zhǎng) 之間 |
| public void reverse () | 反轉(zhuǎn)執(zhí)行動(dòng)畫,如果使用setCurrentPlayTime 跳到了某時(shí)間點(diǎn),將在該時(shí)間點(diǎn)反轉(zhuǎn),否則將從結(jié)束值開(kāi)始反轉(zhuǎn),這個(gè)方法值適用當(dāng)前動(dòng)畫,未來(lái)的動(dòng)畫將不受影響 |
| public AnimatorSet setDuration (long duration) | 設(shè)置動(dòng)畫集中每一個(gè)動(dòng)畫的執(zhí)行時(shí)長(zhǎng),默認(rèn)情況每個(gè)動(dòng)畫執(zhí)行自己的時(shí)長(zhǎng),調(diào)用了這個(gè)方法后,每個(gè)動(dòng)畫的執(zhí)行時(shí)長(zhǎng)將使用設(shè)置值 |
更多方法可見(jiàn)這里。
Interpolator:
時(shí)間插值器定義如何計(jì)算動(dòng)畫中的特定值作為時(shí)間的函數(shù)。例如,可以指定動(dòng)畫在整個(gè)動(dòng)畫中線性發(fā)生,這意味著動(dòng)畫在整個(gè)時(shí)間內(nèi)均勻移動(dòng),或者還可以指定動(dòng)畫以使用非線性時(shí)間,例如,在開(kāi)始時(shí)加速并在結(jié)束時(shí)減速。Android 提供了一些插值器,如果所提供的插值器都不適合需求,可以實(shí)現(xiàn)TimeInterpolator接口并創(chuàng)建自定義插值器。
Android 默認(rèn)提供了9 種插值器
| 類 | 描述 |
|---|---|
| LinearInterpolator | 值隨時(shí)間的變化為線性![]() image.png
|
| AccelerateInterpolator | 加速變化![]() image.png
|
| DecelerateInterpolator | 減速變化![]() image.png
|
| AccelerateDecelerateInterpolator | 先加速后減速變化![]() image.png
|
| AnticipateInterpolator | 先反向變化,再正向快速變化![]() image.png
|
| OvershootInterpolator | 快速變化到超出結(jié)束值一段,再緩慢反向變化回結(jié)束值![]() image.png
|
| BounceInterpolator | 不斷回彈地變化![]() image.png
|
| CycleInterpolator | 正弦函數(shù)變化![]() image.png
|
Evaluator:
Evaluator(估值器)告訴屬性動(dòng)畫系統(tǒng)如何計(jì)算給定屬性的值,它們獲取Animator類提供的時(shí)序數(shù)據(jù)(時(shí)序數(shù)據(jù)的值就是通過(guò)Interpolator 計(jì)算出的),動(dòng)畫的開(kāi)始和結(jié)束值,并根據(jù)此數(shù)據(jù)計(jì)算屬性的動(dòng)畫值。
Android 提供了三種估值器:
| 類 | 描述 |
|---|---|
| IntEvaluator | Int 值屬性的默認(rèn)估值器 |
| FloatEvaluator | Float 值屬性的默認(rèn)估值器 |
| ArgbEvaluator | 屬性值表示顏色時(shí),使用該估值器 |
如果我們需要做動(dòng)畫的屬性的類型不是int 或 float時(shí),就需要實(shí)現(xiàn)TypeEvaluator 接口自定義估值器。
三. 應(yīng)用
1. ValueAnimator
在使用ValueAnimator 實(shí)現(xiàn)動(dòng)畫時(shí),首先要?jiǎng)?chuàng)建一個(gè)Animator 對(duì)象,上面的API 簡(jiǎn)介提到了一系列的of 方法,可以創(chuàng)建針對(duì)不同屬性類型的Animator 對(duì)象
val intAnim = ValueAnimator.ofInt(0, 1000).apply {
duration = 5000
startDelay = 1000
interpolator = BounceInterpolator()
addUpdateListener { // 在監(jiān)聽(tīng)值更新的Listener 里給要做動(dòng)畫的對(duì)象(本例是一個(gè)TextView)的屬性賦值
animText.x = (it.animatedValue as Int).toFloat()
animText.rotationY = (it.animatedValue as Int).toFloat()
}
addListener(object : Animator.AnimatorListener { // 監(jiān)聽(tīng)動(dòng)畫的執(zhí)行事件
override fun onAnimationStart(animation: Animator?) {
println("-------------start")
}
override fun onAnimationEnd(animation: Animator?) {
println("-------------end")
}
override fun onAnimationCancel(animation: Animator?) {
println("-------------cancel")
}
override fun onAnimationRepeat(animation: Animator?) {
println("-------------repeat")
}
})
}
除了用Java/Kotlin 代碼創(chuàng)建外,還可以在XML 中創(chuàng)建動(dòng)畫的相關(guān)設(shè)置:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="1000"
android:valueType="intType"
android:duration="5000"
android:startOffset="1000"
android:repeatCount="1"
android:repeatMode="reverse">
</animator>
加載XML 并創(chuàng)建對(duì)象的代碼如下:
val xmlAnim = AnimatorInflater.loadAnimator(this, R.animator.value_anim)
注意在使用XML 創(chuàng)建Animator 對(duì)象之后,也需要添加Listener 來(lái)動(dòng)態(tài)改變對(duì)象的屬性值。
創(chuàng)建了Animator 對(duì)象之后,若要開(kāi)始動(dòng)畫,只需要調(diào)用start 方法即可。
2. ObjectAnimator
ObjectAnimator 是ValueAnimator 的子類,對(duì)其進(jìn)行了一定程度的封裝,我們不需要再添加監(jiān)聽(tīng)值變化的Listener 來(lái)手動(dòng)為屬性賦值,我們只需在創(chuàng)建ObjectAnimator 對(duì)象時(shí),傳入要改變屬性的對(duì)象及其屬性名即可(但要保證該屬性有對(duì)應(yīng)的setter 方法),如下:
val colorAnim = ObjectAnimator.ofArgb(textView, "textColor", Color.parseColor("#dc5f26"), Color.parseColor("#2684DC")).apply {
duration = 5000
}
同樣,ObjectAnimator 的相關(guān)設(shè)置也可以通過(guò)XML 來(lái)寫,
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="800"
android:valueType="floatType"
android:duration="5000"
android:startOffset="1000"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:repeatCount="1"
android:repeatMode="restart"
android:propertyXName="x"
android:propertyYName="y"
android:pathData="M10,10Q200,100,400,600Q600,100,800,0Z">
</objectAnimator>
同樣需要加載對(duì)象
val xmlAnim = AnimatorInflater.loadAnimator(this, R.animator.obj_anim)
xmlAnim.setTarget(textView) // 設(shè)置動(dòng)畫作用的對(duì)象
開(kāi)始動(dòng)畫只需調(diào)用start 方法即可。
我們知道,屬性動(dòng)畫的動(dòng)畫效果就是通過(guò)不斷改變對(duì)象的屬性值來(lái)實(shí)現(xiàn)的,所以要比補(bǔ)間動(dòng)畫更靈活,那么補(bǔ)間動(dòng)畫能實(shí)現(xiàn)的View 的四種動(dòng)畫效果,放在屬性動(dòng)畫里應(yīng)該改變哪些值呢,如下表
| 屬性 | 作用 | 數(shù)值類型 |
|---|---|---|
| alpha | 控制View的透明度 | float |
| translationX | 控制X方向的位移 | float |
| translationY | 控制Y方向的位移 | float |
| translationZ | 控制Z方向的位移 | float |
| scaleX | 控制X方向的縮放倍數(shù) | float |
| scaleY | 控制Y方向的縮放倍數(shù) | float |
| rotation | 控制以屏幕方向(可以理解為Z 軸)為軸的旋轉(zhuǎn)度數(shù) | float |
| rotationX | 控制以X軸為軸的旋轉(zhuǎn)度數(shù) | float |
| rotationY | 控制以Y軸為軸的旋轉(zhuǎn)度數(shù) | float |
這些都是View 的屬性,通過(guò)對(duì)他們的改變就可以實(shí)現(xiàn)一些基本的動(dòng)畫效果。
3. AnimatorSet
AnimatorSet 可以將多個(gè)動(dòng)畫組合起來(lái)進(jìn)行播放,并且可以給這些動(dòng)畫設(shè)置先后順序等
val path = Path().apply {
moveTo(10f, 10f)
quadTo(200f, 100f, 400f, 600f)
quadTo(600f, 100f,800f, 0f)
}
val pathAnim = ObjectAnimator.ofFloat(textView, View.X, View.Y, path).apply { // 創(chuàng)建一個(gè)讓View 按照指定Path 運(yùn)動(dòng)的動(dòng)畫
duration = 3000
interpolator = AccelerateDecelerateInterpolator()
}
// 顏色改變的動(dòng)畫
val colorAnim = ObjectAnimator.ofArgb(textView, "textColor", Color.parseColor("#dc5f26"), Color.parseColor("#2684DC")).apply {
duration = 5000
}
// 改變橫坐標(biāo)的動(dòng)畫
val intAnim = ObjectAnimator.ofFloat(textView, "x", 0f).apply {
duration = 3000
}
val animSet = AnimatorSet()
animSet.play(pathAnim) // 調(diào)用play 來(lái)播放
animSet.play(colorAnim).with(intAnim).after(pathAnim).after(2000) // with 表示同時(shí)播放,after 表示在xxx 之后播放,before 表示在xxx 之前播放
animSet.start() // 開(kāi)始動(dòng)畫







