一. 簡(jiǎn)介
屬性動(dòng)畫(huà)(Property Animation)是Android 3.0(API 11)之后提供的動(dòng)畫(huà)框架,在之前Android 提供了 幀動(dòng)畫(huà)(Frame Animation) 和 補(bǔ)間動(dòng)畫(huà)(Tween Animation),幀動(dòng)畫(huà)就是把一個(gè)動(dòng)畫(huà)分成多張圖片撬讽,然后把這些圖片連貫起來(lái)播放游昼,原理和動(dòng)畫(huà)片類(lèi)似烘豌;補(bǔ)間動(dòng)畫(huà)是針對(duì)View 進(jìn)行的平移廊佩、旋轉(zhuǎn)标锄、縮放料皇、透明度變化 等動(dòng)畫(huà)效果践剂;屬性動(dòng)畫(huà)則是通過(guò)改變屬性值來(lái)實(shí)現(xiàn)動(dòng)畫(huà)效果逊脯,不再局限于View军洼。
屬性動(dòng)畫(huà)和補(bǔ)間動(dòng)畫(huà)的對(duì)比:
作用對(duì)象
補(bǔ)間動(dòng)畫(huà)的作用對(duì)象僅為View歉眷,而屬性動(dòng)畫(huà)則可作用于非View 對(duì)象汗捡。有些情況下扇住,可能需要對(duì)View 的某個(gè)對(duì)象進(jìn)行操作,而不是整個(gè)View 對(duì)象女阀,比如動(dòng)態(tài)改變View 的顏色浸策,此時(shí)補(bǔ)間動(dòng)畫(huà)便無(wú)法實(shí)現(xiàn)庸汗。對(duì)View 的影響
補(bǔ)間動(dòng)畫(huà)只改變View 的視覺(jué)效果蚯舱,并不改變View 的屬性枉昏,比如將一個(gè)Button 從屏幕左上角 移動(dòng)至 右下角凶掰,使用補(bǔ)間動(dòng)畫(huà)的話沧竟,移動(dòng)完成之后 Button 的點(diǎn)擊區(qū)域還是左上角畅涂,因?yàn)樗奈恢脤傩圆](méi)有改變立宜。而屬性動(dòng)畫(huà)橙数,顧名思義,改變的就是屬性帅戒。動(dòng)畫(huà)效果
補(bǔ)間動(dòng)畫(huà)效果比較單一灯帮,僅支持平移、旋轉(zhuǎn)逻住、縮放钟哥、透明度變化,而屬性動(dòng)畫(huà)比較靈活瞎访,可以實(shí)現(xiàn)多種復(fù)雜的動(dòng)畫(huà)效果。
在現(xiàn)在的開(kāi)發(fā)過(guò)程中扒秸,由于屬性動(dòng)畫(huà)已經(jīng)可以實(shí)現(xiàn)幾乎所有補(bǔ)間動(dòng)畫(huà)能實(shí)現(xiàn)的功能播演,所以我們最常用的還是屬性動(dòng)畫(huà),本篇將簡(jiǎn)介屬性動(dòng)畫(huà)的基本使用伴奥。
二. 工作原理及相關(guān)類(lèi)
1. 屬性動(dòng)畫(huà)的工作過(guò)程
從上面的工作過(guò)程可以看到宾巍,屬性動(dòng)畫(huà)框架中最常用也是最重要的幾個(gè)類(lèi),就是ValueAnimator渔伯、ObjectAnimator顶霞、Interpolator 的實(shí)現(xiàn)類(lèi) 和 TypeEvaluator 的實(shí)現(xiàn)類(lèi)。
2. 相關(guān)類(lèi)簡(jiǎn)介
Animators:
1)ValueAnimator,屬性動(dòng)畫(huà)的主要計(jì)時(shí)引擎选浑,它還計(jì)算要設(shè)置動(dòng)畫(huà)的屬性的值蓝厌。它具有計(jì)算動(dòng)畫(huà)值的所有核心功能,包含每個(gè)動(dòng)畫(huà)的持續(xù)時(shí)間古徒,有關(guān)動(dòng)畫(huà)是否重復(fù)進(jìn)行拓提,更新屬性值的事件偵聽(tīng)器以及設(shè)置自定義類(lèi)型的估值器功能。屬性動(dòng)畫(huà)有兩個(gè)部分:計(jì)算動(dòng)畫(huà)過(guò)程中的屬性值隧膘,給對(duì)象和屬性上設(shè)置這些值代态。ValueAnimator 僅負(fù)責(zé)計(jì)算值,因此必須監(jiān)聽(tīng)ValueAnimator 計(jì)算的值的更新疹吃,并使用按照自己的邏輯修改要設(shè)置動(dòng)畫(huà)的對(duì)象蹦疑。
主要方法:
方法簽名 | 用途 |
---|---|
public static ValueAnimator ofInt (int... values) | ofInt() 作用有兩個(gè): 1. 創(chuàng)建動(dòng)畫(huà)實(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萨驶,以此類(lèi)推歉摧。 ValueAnimator.ofInt() 內(nèi)置了整型估值器,直接采用默認(rèn)的腔呜,不需要設(shè)置叁温,即默認(rèn)設(shè)置了如何從初始值 過(guò)渡到 結(jié)束值。 |
public static ValueAnimator ofFloat (float... values) | 類(lèi)似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)畫(huà)持續(xù)時(shí)長(zhǎng),單位毫秒 |
public void setRepeatCount (int value) | 設(shè)置動(dòng)畫(huà)重復(fù)次數(shù) |
public void setRepeatMode (int value) | 設(shè)置動(dòng)畫(huà)重復(fù)模式咖刃,有兩種取值:RESTART 或 REVERSE,顧名思義就是重新開(kāi)始 和 倒著執(zhí)行 |
public void setStartDelay (long startDelay) | 設(shè)置延遲多長(zhǎng)時(shí)間開(kāi)始動(dòng)畫(huà)憾筏,單位毫秒 |
public boolean isRunning () | 判斷動(dòng)畫(huà)是否正在運(yùn)行 |
public void addUpdateListener (ValueAnimator.AnimatorUpdateListener listener) | 添加值變化時(shí)的監(jiān)聽(tīng)事件嚎杨,若要使用ValueAnimator 實(shí)現(xiàn)動(dòng)畫(huà)就必須要添加一個(gè)Listener,在數(shù)據(jù)變化之后改變對(duì)象屬性來(lái)實(shí)現(xiàn)動(dòng)畫(huà) |
public void start () | 開(kāi)始動(dòng)畫(huà)氧腰,如果沒(méi)有設(shè)置延時(shí)開(kāi)始枫浙,動(dòng)畫(huà)會(huì)立即開(kāi)始,設(shè)置了則會(huì)延時(shí)一段時(shí)間后開(kāi)始 |
public void cancel () / public void end () | 取消動(dòng)畫(huà) / 結(jié)束動(dòng)畫(huà)古拴,兩者的區(qū)別就是調(diào)用cancel 會(huì)停止在當(dāng)前狀態(tài)箩帚,end 會(huì)將屬性值設(shè)置為結(jié)束值 |
public void addListener (Animator.AnimatorListener listener) | 是父類(lèi)Animator 的方法,添加Listener黄痪,監(jiān)聽(tīng)動(dòng)畫(huà)開(kāi)始紧帕、結(jié)束、取消、重復(fù)等事件 |
public void pause () / public void resume () | 暫停 / 重新開(kāi)始動(dòng)畫(huà)是嗜,調(diào)用這兩個(gè)方法的線程必須和動(dòng)畫(huà)開(kāi)始的線程相同愈案,另外,如果對(duì)一個(gè)沒(méi)有暫停的動(dòng)畫(huà)調(diào)用resume 方法將會(huì)被忽略 |
以上鹅搪,只是比較常用的方法站绪,還有許多諸如getter 等方法,這里不一一列舉丽柿,詳情可參官方文檔恢准。
2)ObjectAnimator,是ValueAnimator的子類(lèi)甫题,允許我們將目標(biāo)對(duì)象和對(duì)象屬性設(shè)置為動(dòng)畫(huà)馁筐,它在計(jì)算動(dòng)畫(huà)的新值時(shí)會(huì)相應(yīng)地更新屬性,我們?cè)诖蠖鄶?shù)情況下可以使用ObjectAnimator幔睬,因?yàn)樗沟迷谀繕?biāo)對(duì)象上設(shè)置動(dòng)畫(huà)值的過(guò)程變得更加容易眯漩。但是,ObjectAnimator還有一些限制麻顶,例如要求在目標(biāo)對(duì)象上存在特定的acessor方法赦抖。
主要方法和ValueAnimator 類(lèi)似,只是創(chuàng)建對(duì)象的ofXXX 方法有些不同辅肾,常用如下
方法簽名 | 用途 |
---|---|
public static ObjectAnimator ofArgb (Object target, String propertyName, int... values) | 構(gòu)造并返回一個(gè)在顏色值之間設(shè)置動(dòng)畫(huà)的ObjectAnimator队萤,values 傳單個(gè)值意味著該值是動(dòng)畫(huà)的結(jié)束值,在這種情況下矫钓,起始值將從正在執(zhí)行動(dòng)畫(huà)的屬性的值和第一次調(diào)用start() 時(shí)的目標(biāo)對(duì)象產(chǎn)生要尔。兩個(gè)值的話意味著起始值和結(jié)束值。兩個(gè)以上的值意味著起始值新娜,沿途的動(dòng)畫(huà)值和結(jié)束值 |
public static ObjectAnimator ofFloat (Object target, String xPropertyName, String yPropertyName, Path path) | 使用兩個(gè)屬性沿Path設(shè)置動(dòng)畫(huà)赵辕,“路徑”動(dòng)畫(huà)以二維方式移動(dòng),將坐標(biāo)(x概龄,y)設(shè)置為動(dòng)畫(huà)还惠,以跟隨線條。坐標(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 類(lèi)似,只不過(guò)參數(shù)值的類(lèi)型是int 值 |
需要注意的是衰粹,在給對(duì)象的指定屬性設(shè)置值時(shí)锣光,要求對(duì)象內(nèi)必須有屬性對(duì)應(yīng)的getter 和setter。更多方法可以查文檔铝耻。
3)AnimatorSet
提供一種將動(dòng)畫(huà)分組在一起的機(jī)制誊爹,以便它們相互運(yùn)行,可以將動(dòng)畫(huà)設(shè)置為一起播放,按順序播放替废,或在指定的延遲后播放箍铭。主要方法如下
方法簽名 | 用途 |
---|---|
public AnimatorSet.Builder play (Animator anim) | 此方法創(chuàng)建一個(gè)Builder對(duì)象,用于設(shè)置動(dòng)畫(huà)之間的播放約束椎镣。這個(gè)初始的play() 方法告訴Builder動(dòng)畫(huà)诈火,它是對(duì)Builder的后續(xù)命令的依賴(lài)。例如状答,調(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)建依賴(lài)關(guān)系的動(dòng)畫(huà)的唯一方法充活,因此對(duì)Builder 中各種函數(shù)的連續(xù)調(diào)用都將引用play() 中提供的初始參數(shù)作為其他動(dòng)畫(huà)的依賴(lài)關(guān)系。例如蜡娶,當(dāng)a1結(jié)束時(shí)混卵,調(diào)用play(a1).before(a2).before(a3) 將同時(shí)播放a2和a3; 它沒(méi)有在a2 和a3 之間建立依賴(lài)關(guān)系。 |
public void playSequentially (List<Animator> items) | 順序執(zhí)行動(dòng)畫(huà)列表 |
public void playTogether (Collection<Animator> items) | 同時(shí)播放集合中的動(dòng)畫(huà) |
public void setCurrentPlayTime (long playTime) | 設(shè)置時(shí)間進(jìn)度到指定的時(shí)間點(diǎn)窖张,值應(yīng)該在0 和 動(dòng)畫(huà)集結(jié)束的總時(shí)長(zhǎng) 之間 |
public void reverse () | 反轉(zhuǎn)執(zhí)行動(dòng)畫(huà)幕随,如果使用setCurrentPlayTime 跳到了某時(shí)間點(diǎn),將在該時(shí)間點(diǎn)反轉(zhuǎn)宿接,否則將從結(jié)束值開(kāi)始反轉(zhuǎn)赘淮,這個(gè)方法值適用當(dāng)前動(dòng)畫(huà),未來(lái)的動(dòng)畫(huà)將不受影響 |
public AnimatorSet setDuration (long duration) | 設(shè)置動(dòng)畫(huà)集中每一個(gè)動(dòng)畫(huà)的執(zhí)行時(shí)長(zhǎng)睦霎,默認(rèn)情況每個(gè)動(dòng)畫(huà)執(zhí)行自己的時(shí)長(zhǎng)梢卸,調(diào)用了這個(gè)方法后,每個(gè)動(dòng)畫(huà)的執(zhí)行時(shí)長(zhǎng)將使用設(shè)置值 |
更多方法可見(jiàn)這里副女。
Interpolator:
時(shí)間插值器定義如何計(jì)算動(dòng)畫(huà)中的特定值作為時(shí)間的函數(shù)蛤高。例如,可以指定動(dòng)畫(huà)在整個(gè)動(dòng)畫(huà)中線性發(fā)生肮塞,這意味著動(dòng)畫(huà)在整個(gè)時(shí)間內(nèi)均勻移動(dòng)襟齿,或者還可以指定動(dòng)畫(huà)以使用非線性時(shí)間姻锁,例如枕赵,在開(kāi)始時(shí)加速并在結(jié)束時(shí)減速。Android 提供了一些插值器位隶,如果所提供的插值器都不適合需求拷窜,可以實(shí)現(xiàn)TimeInterpolator接口并創(chuàng)建自定義插值器。
Android 默認(rèn)提供了9 種插值器
類(lèi) | 描述 |
---|---|
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)畫(huà)系統(tǒng)如何計(jì)算給定屬性的值赋荆,它們獲取Animator類(lèi)提供的時(shí)序數(shù)據(jù)(時(shí)序數(shù)據(jù)的值就是通過(guò)Interpolator 計(jì)算出的),動(dòng)畫(huà)的開(kāi)始和結(jié)束值懊昨,并根據(jù)此數(shù)據(jù)計(jì)算屬性的動(dòng)畫(huà)值窄潭。
Android 提供了三種估值器:
類(lèi) | 描述 |
---|---|
IntEvaluator | Int 值屬性的默認(rèn)估值器 |
FloatEvaluator | Float 值屬性的默認(rèn)估值器 |
ArgbEvaluator | 屬性值表示顏色時(shí),使用該估值器 |
如果我們需要做動(dòng)畫(huà)的屬性的類(lèi)型不是int 或 float時(shí)酵颁,就需要實(shí)現(xiàn)TypeEvaluator 接口自定義估值器嫉你。
三. 應(yīng)用
1. ValueAnimator
在使用ValueAnimator 實(shí)現(xiàn)動(dòng)畫(huà)時(shí),首先要?jiǎng)?chuàng)建一個(gè)Animator 對(duì)象躏惋,上面的API 簡(jiǎn)介提到了一系列的of 方法幽污,可以創(chuàng)建針對(duì)不同屬性類(lèi)型的Animator 對(duì)象
val intAnim = ValueAnimator.ofInt(0, 1000).apply {
duration = 5000
startDelay = 1000
interpolator = BounceInterpolator()
addUpdateListener { // 在監(jiān)聽(tīng)值更新的Listener 里給要做動(dòng)畫(huà)的對(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)畫(huà)的執(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)畫(huà)的相關(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)畫(huà)扁位,只需要調(diào)用start 方法即可准潭。
2. ObjectAnimator
ObjectAnimator 是ValueAnimator 的子類(lèi),對(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)寫(xiě),
<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)畫(huà)作用的對(duì)象
開(kāi)始動(dòng)畫(huà)只需調(diào)用start 方法即可般卑。
我們知道武鲁,屬性動(dòng)畫(huà)的動(dòng)畫(huà)效果就是通過(guò)不斷改變對(duì)象的屬性值來(lái)實(shí)現(xiàn)的,所以要比補(bǔ)間動(dòng)畫(huà)更靈活蝠检,那么補(bǔ)間動(dòng)畫(huà)能實(shí)現(xiàn)的View 的四種動(dòng)畫(huà)效果沐鼠,放在屬性動(dòng)畫(huà)里應(yīng)該改變哪些值呢,如下表
屬性 | 作用 | 數(shù)值類(lèi)型 |
---|---|---|
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)畫(huà)效果饲梭。
3. AnimatorSet
AnimatorSet 可以將多個(gè)動(dòng)畫(huà)組合起來(lái)進(jìn)行播放,并且可以給這些動(dòng)畫(huà)設(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)畫(huà)
duration = 3000
interpolator = AccelerateDecelerateInterpolator()
}
// 顏色改變的動(dòng)畫(huà)
val colorAnim = ObjectAnimator.ofArgb(textView, "textColor", Color.parseColor("#dc5f26"), Color.parseColor("#2684DC")).apply {
duration = 5000
}
// 改變橫坐標(biāo)的動(dòng)畫(huà)
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)畫(huà)