Android 屬性動畫

一. 簡介

屬性動畫(Property Animation)是Android 3.0(API 11)之后提供的動畫框架,在之前Android 提供了 幀動畫(Frame Animation) 和 補(bǔ)間動畫(Tween Animation),幀動畫就是把一個動畫分成多張圖片,然后把這些圖片連貫起來播放,原理和動畫片類似;補(bǔ)間動畫是針對View 進(jìn)行的平移、旋轉(zhuǎn)、縮放、透明度變化 等動畫效果;屬性動畫則是通過改變屬性值來實(shí)現(xiàn)動畫效果,不再局限于View。

屬性動畫和補(bǔ)間動畫的對比:

  • 作用對象
    補(bǔ)間動畫的作用對象僅為View,而屬性動畫則可作用于非View 對象。有些情況下,可能需要對View 的某個對象進(jìn)行操作,而不是整個View 對象,比如動態(tài)改變View 的顏色,此時補(bǔ)間動畫便無法實(shí)現(xiàn)。

  • 對View 的影響
    補(bǔ)間動畫只改變View 的視覺效果,并不改變View 的屬性,比如將一個Button 從屏幕左上角 移動至 右下角,使用補(bǔ)間動畫的話,移動完成之后 Button 的點(diǎn)擊區(qū)域還是左上角,因?yàn)樗奈恢脤傩圆]有改變。而屬性動畫,顧名思義,改變的就是屬性。

  • 動畫效果
    補(bǔ)間動畫效果比較單一,僅支持平移、旋轉(zhuǎn)、縮放、透明度變化,而屬性動畫比較靈活,可以實(shí)現(xiàn)多種復(fù)雜的動畫效果。

在現(xiàn)在的開發(fā)過程中,由于屬性動畫已經(jīng)可以實(shí)現(xiàn)幾乎所有補(bǔ)間動畫能實(shí)現(xiàn)的功能,所以我們最常用的還是屬性動畫,本篇將簡介屬性動畫的基本使用。

二. 工作原理及相關(guān)類

1. 屬性動畫的工作過程
工作過程.png

從上面的工作過程可以看到,屬性動畫框架中最常用也是最重要的幾個類,就是ValueAnimator、ObjectAnimator、Interpolator 的實(shí)現(xiàn)類 和 TypeEvaluator 的實(shí)現(xiàn)類。

2. 相關(guān)類簡介

Animators:
1)ValueAnimator,屬性動畫的主要計時引擎,它還計算要設(shè)置動畫的屬性的值。它具有計算動畫值的所有核心功能,包含每個動畫的持續(xù)時間,有關(guān)動畫是否重復(fù)進(jìn)行,更新屬性值的事件偵聽器以及設(shè)置自定義類型的估值器功能。屬性動畫有兩個部分:計算動畫過程中的屬性值,給對象和屬性上設(shè)置這些值。ValueAnimator 僅負(fù)責(zé)計算值,因此必須監(jiān)聽ValueAnimator 計算的值的更新,并使用按照自己的邏輯修改要設(shè)置動畫的對象。

主要方法:

方法簽名 用途
public static ValueAnimator ofInt (int... values) ofInt() 作用有兩個:
1. 創(chuàng)建動畫實(shí)例
2. 將傳入的多個Int參數(shù)進(jìn)行平滑過渡: 如果傳入0和1,表示將值從0平滑過渡到1,如果傳入了3個Int參數(shù) a,b,c ,則是先從a平滑過渡到b,再從b平滑過渡到C,以此類推。
ValueAnimator.ofInt() 內(nèi)置了整型估值器,直接采用默認(rèn)的,不需要設(shè)置,即默認(rèn)設(shè)置了如何從初始值 過渡到 結(jié)束值。
public static ValueAnimator ofFloat (float... values) 類似ofInt
public static ValueAnimator ofArgb (int... values) api 21 時加入,傳入的參數(shù)是顏色的int 值,提供顏色值的平滑變換,和ofInt 的區(qū)別在于使用的估值器不同
public static ValueAnimator ofObject (TypeEvaluator evaluator, Object... values) 可傳入對象作為參數(shù)創(chuàng)建Animator 實(shí)例,由于可以傳任意對象,所以需要自己提供估值器
public ValueAnimator setDuration (long duration) 設(shè)置動畫持續(xù)時長,單位毫秒
public void setRepeatCount (int value) 設(shè)置動畫重復(fù)次數(shù)
public void setRepeatMode (int value) 設(shè)置動畫重復(fù)模式,有兩種取值:RESTART 或 REVERSE,顧名思義就是重新開始 和 倒著執(zhí)行
public void setStartDelay (long startDelay) 設(shè)置延遲多長時間開始動畫,單位毫秒
public boolean isRunning () 判斷動畫是否正在運(yùn)行
public void addUpdateListener (ValueAnimator.AnimatorUpdateListener listener) 添加值變化時的監(jiān)聽事件,若要使用ValueAnimator 實(shí)現(xiàn)動畫就必須要添加一個Listener,在數(shù)據(jù)變化之后改變對象屬性來實(shí)現(xiàn)動畫
public void start () 開始動畫,如果沒有設(shè)置延時開始,動畫會立即開始,設(shè)置了則會延時一段時間后開始
public void cancel () / public void end () 取消動畫 / 結(jié)束動畫,兩者的區(qū)別就是調(diào)用cancel 會停止在當(dāng)前狀態(tài),end 會將屬性值設(shè)置為結(jié)束值
public void addListener (Animator.AnimatorListener listener) 是父類Animator 的方法,添加Listener,監(jiān)聽動畫開始、結(jié)束、取消、重復(fù)等事件
public void pause () / public void resume () 暫停 / 重新開始動畫,調(diào)用這兩個方法的線程必須和動畫開始的線程相同,另外,如果對一個沒有暫停的動畫調(diào)用resume 方法將會被忽略

以上,只是比較常用的方法,還有許多諸如getter 等方法,這里不一一列舉,詳情可參官方文檔

2)ObjectAnimator,是ValueAnimator的子類,允許我們將目標(biāo)對象和對象屬性設(shè)置為動畫,它在計算動畫的新值時會相應(yīng)地更新屬性,我們在大多數(shù)情況下可以使用ObjectAnimator,因?yàn)樗沟迷谀繕?biāo)對象上設(shè)置動畫值的過程變得更加容易。但是,ObjectAnimator還有一些限制,例如要求在目標(biāo)對象上存在特定的acessor方法。

主要方法和ValueAnimator 類似,只是創(chuàng)建對象的ofXXX 方法有些不同,常用如下

方法簽名 用途
public static ObjectAnimator ofArgb (Object target, String propertyName, int... values) 構(gòu)造并返回一個在顏色值之間設(shè)置動畫的ObjectAnimator,values 傳單個值意味著該值是動畫的結(jié)束值,在這種情況下,起始值將從正在執(zhí)行動畫的屬性的值和第一次調(diào)用start() 時的目標(biāo)對象產(chǎn)生。兩個值的話意味著起始值和結(jié)束值。兩個以上的值意味著起始值,沿途的動畫值和結(jié)束值
public static ObjectAnimator ofFloat (Object target, String xPropertyName, String yPropertyName, Path path) 使用兩個屬性沿Path設(shè)置動畫,“路徑”動畫以二維方式移動,將坐標(biāo)(x,y)設(shè)置為動畫,以跟隨線條。坐標(biāo)是浮點(diǎn)數(shù),它們被分別設(shè)置到由xPropertyName和yPropertyName指定的屬性。
public static ObjectAnimator ofFloat (Object target, String propertyName, float... values) 為指定對象target 的指定屬性propertyName 設(shè)置值
public static ObjectAnimator ofInt (T target, Property<T, Integer> xProperty, Property<T, Integer> yProperty, Path path) 和上面提到的ofFloat 類似,只不過參數(shù)值的類型是int 值

需要注意的是,在給對象的指定屬性設(shè)置值時,要求對象內(nèi)必須有屬性對應(yīng)的getter 和setter。更多方法可以查文檔

3)AnimatorSet

提供一種將動畫分組在一起的機(jī)制,以便它們相互運(yùn)行,可以將動畫設(shè)置為一起播放,按順序播放,或在指定的延遲后播放。主要方法如下

方法簽名 用途
public AnimatorSet.Builder play (Animator anim) 此方法創(chuàng)建一個Builder對象,用于設(shè)置動畫之間的播放約束。這個初始的play() 方法告訴Builder動畫,它是對Builder的后續(xù)命令的依賴。例如,調(diào)用play(a1).with(a2) 將AnimatorSet 設(shè)置為同時播放a1和a2,play(a1).before(a2) 設(shè)置AnimatorSet首先播放a1,然后播放a2,而 play(a1).after(a2) 將AnimatorSet 設(shè)置為先播放a2,然后播放a1。請注意,play() 是告訴Builder 創(chuàng)建依賴關(guān)系的動畫的唯一方法,因此對Builder 中各種函數(shù)的連續(xù)調(diào)用都將引用play() 中提供的初始參數(shù)作為其他動畫的依賴關(guān)系。例如,當(dāng)a1結(jié)束時,調(diào)用play(a1).before(a2).before(a3) 將同時播放a2和a3; 它沒有在a2 和a3 之間建立依賴關(guān)系。
public void playSequentially (List<Animator> items) 順序執(zhí)行動畫列表
public void playTogether (Collection<Animator> items) 同時播放集合中的動畫
public void setCurrentPlayTime (long playTime) 設(shè)置時間進(jìn)度到指定的時間點(diǎn),值應(yīng)該在0 和 動畫集結(jié)束的總時長 之間
public void reverse () 反轉(zhuǎn)執(zhí)行動畫,如果使用setCurrentPlayTime 跳到了某時間點(diǎn),將在該時間點(diǎn)反轉(zhuǎn),否則將從結(jié)束值開始反轉(zhuǎn),這個方法值適用當(dāng)前動畫,未來的動畫將不受影響
public AnimatorSet setDuration (long duration) 設(shè)置動畫集中每一個動畫的執(zhí)行時長,默認(rèn)情況每個動畫執(zhí)行自己的時長,調(diào)用了這個方法后,每個動畫的執(zhí)行時長將使用設(shè)置值

更多方法可見這里。

Interpolator:

時間插值器定義如何計算動畫中的特定值作為時間的函數(shù)。例如,可以指定動畫在整個動畫中線性發(fā)生,這意味著動畫在整個時間內(nèi)均勻移動,或者還可以指定動畫以使用非線性時間,例如,在開始時加速并在結(jié)束時減速。Android 提供了一些插值器,如果所提供的插值器都不適合需求,可以實(shí)現(xiàn)TimeInterpolator接口并創(chuàng)建自定義插值器。

Android 默認(rèn)提供了9 種插值器

描述
LinearInterpolator 值隨時間的變化為線性
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(估值器)告訴屬性動畫系統(tǒng)如何計算給定屬性的值,它們獲取Animator類提供的時序數(shù)據(jù)(時序數(shù)據(jù)的值就是通過Interpolator 計算出的),動畫的開始和結(jié)束值,并根據(jù)此數(shù)據(jù)計算屬性的動畫值。

Android 提供了三種估值器:

描述
IntEvaluator Int 值屬性的默認(rèn)估值器
FloatEvaluator Float 值屬性的默認(rèn)估值器
ArgbEvaluator 屬性值表示顏色時,使用該估值器

如果我們需要做動畫的屬性的類型不是int 或 float時,就需要實(shí)現(xiàn)TypeEvaluator 接口自定義估值器。

三. 應(yīng)用

1. ValueAnimator

在使用ValueAnimator 實(shí)現(xiàn)動畫時,首先要創(chuàng)建一個Animator 對象,上面的API 簡介提到了一系列的of 方法,可以創(chuàng)建針對不同屬性類型的Animator 對象

    val intAnim = ValueAnimator.ofInt(0, 1000).apply {
            duration = 5000
            startDelay = 1000
            interpolator = BounceInterpolator()
            addUpdateListener {         // 在監(jiān)聽值更新的Listener 里給要做動畫的對象(本例是一個TextView)的屬性賦值
                animText.x = (it.animatedValue as Int).toFloat()
                animText.rotationY = (it.animatedValue as Int).toFloat()
            }
            addListener(object : Animator.AnimatorListener {            // 監(jiān)聽動畫的執(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)建動畫的相關(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)建對象的代碼如下:

val xmlAnim = AnimatorInflater.loadAnimator(this, R.animator.value_anim)

注意在使用XML 創(chuàng)建Animator 對象之后,也需要添加Listener 來動態(tài)改變對象的屬性值。

創(chuàng)建了Animator 對象之后,若要開始動畫,只需要調(diào)用start 方法即可。

2. ObjectAnimator

ObjectAnimator 是ValueAnimator 的子類,對其進(jìn)行了一定程度的封裝,我們不需要再添加監(jiān)聽值變化的Listener 來手動為屬性賦值,我們只需在創(chuàng)建ObjectAnimator 對象時,傳入要改變屬性的對象及其屬性名即可(但要保證該屬性有對應(yīng)的setter 方法),如下:

val colorAnim = ObjectAnimator.ofArgb(textView, "textColor", Color.parseColor("#dc5f26"), Color.parseColor("#2684DC")).apply {
    duration = 5000
}

同樣,ObjectAnimator 的相關(guān)設(shè)置也可以通過XML 來寫,

<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>

同樣需要加載對象

val xmlAnim = AnimatorInflater.loadAnimator(this, R.animator.obj_anim)
xmlAnim.setTarget(textView)                 // 設(shè)置動畫作用的對象

開始動畫只需調(diào)用start 方法即可。

我們知道,屬性動畫的動畫效果就是通過不斷改變對象的屬性值來實(shí)現(xiàn)的,所以要比補(bǔ)間動畫更靈活,那么補(bǔ)間動畫能實(shí)現(xiàn)的View 的四種動畫效果,放在屬性動畫里應(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 的屬性,通過對他們的改變就可以實(shí)現(xiàn)一些基本的動畫效果。

3. AnimatorSet

AnimatorSet 可以將多個動畫組合起來進(jìn)行播放,并且可以給這些動畫設(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)建一個讓View 按照指定Path 運(yùn)動的動畫
            duration = 3000
            interpolator = AccelerateDecelerateInterpolator()
        }

                // 顏色改變的動畫
        val colorAnim = ObjectAnimator.ofArgb(textView, "textColor", Color.parseColor("#dc5f26"), Color.parseColor("#2684DC")).apply {
            duration = 5000
        }

                // 改變橫坐標(biāo)的動畫
        val intAnim = ObjectAnimator.ofFloat(textView, "x", 0f).apply {
            duration = 3000
        }

        val animSet = AnimatorSet()
        animSet.play(pathAnim)          // 調(diào)用play 來播放
        animSet.play(colorAnim).with(intAnim).after(pathAnim).after(2000)           // with 表示同時播放,after 表示在xxx 之后播放,before 表示在xxx 之前播放

                animSet.start()                         // 開始動畫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容