初識屬性動畫——使用Animator建立動畫

xingfeng_coder發表於2019-02-22

屬性動畫不僅僅能作用於View,而能作用於任何物件。

與ViewAnimation的區別

ViewAnimation只支援幾種動畫:scale、transition、rotate、alpha四種型別。並且缺陷是隻是改變了顯示位置,實際位置並沒有改變。
一個demo解釋一切,如下圖:

與ViewAnimation的區別

Hello按鈕使用ViewAnimation進行移動,點選事件在移動後,但是響應還在最初的位置;而屬性動畫移動的World按鈕則不同,響應是跟著按鈕走的。

屬性動畫原理

屬性動畫的一些引數

建立一個屬性動畫,一般需要設定幾個引數,如下:

  • duration:動畫的持續時長,預設300ms
  • Time interpolation:時間插值器,是一個函式,property=f(time),隨著時間計算屬性的函式
  • 重複次數和行為:可以指定動畫是否重複,以及重複次數;也可以指定動畫是否reverse
  • AnimatorSet:可以組合多個動畫,同時作用or分批作用
  • 幀重新整理延遲:可以指定多久重新整理一幀動畫,預設值是每10s重新整理一幀,但實際上的值還是要依賴於系統的實際執行情況。

原理說明

以官方圖為例:

線性Demo

一個動畫,40ms,從左向右移動40pixel,每隔10ms,新的幀被畫出來了,動畫停止時,View停在了最終位置。
上面的例子是一個線性的效果,時間插值器的函式是:

pixel=1*time
複製程式碼

下面是一個非線性的例子,如圖:

非線性Demo

這個只需要改變插值器即可,可以看到先加速到一半,再開始減速。
下面開始正式說明屬性動畫的原理,首先看下圖:
原理圖

可以看到核心是ValueAnimator這個類會追蹤動畫的時長,當前屬性值。
ValueAnimator封裝了TimeInterpolation和TypeEvaluator,TimeInterpolation用來計算每一幀對應時間的屬性,TypeEvaluator用來從動畫中獲取屬性值。
建立一個動畫並開啟後,屬性動畫主要有三步操作:

  1. 根據時間流失,得到一個已過時間因子,這個值的範圍是[0,1],以上面的例子為例,總時長40ms,而每一幀10ms,第一幀的已過時間因子就是0.25
  2. 得到已過時間因子後,ValueAnimator會呼叫TimeInterpolation計算得到屬性因子,以上面的例子為例,已過時間因子是0.25,線性插值器的計算結果也是0.25
  3. 得到了插值器的結果後,ValueAnimator會呼叫TypeEvaluator計算具體的屬性值,然後改變View的該屬性值。

每一幀,經過這麼計算,就是屬性動畫的原理。

關於API

主要是ValueAnimator,ObjectAnimatorAnimatorSet,類結構圖如下所示:

Animator類結構圖

使用

Animator和Animation一樣,既可以程式碼實現,也可以在xml中定義,下面分別說明兩種方式分別是如何操作的。

使用ValueAnimator

ValueAnimator有一些靜態方法,ofInt、ofFloat、ofObject。
先看效果,

ValueAnimator效果

  1. xml定義動畫
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
          android:duration="3000"
          android:interpolator="@android:anim/accelerate_interpolator"
          android:valueFrom="0"
          android:valueTo="1000"
          android:valueType="floatType"
    />
複製程式碼

配合程式碼:

val animatorFromXml = AnimatorInflater.loadAnimator(this, R.animator.value_move_view)
        btnMoveFromXml.setOnClickListener {
            if (animatorFromXml is ValueAnimator) {
                animatorFromXml.apply {
                    addUpdateListener {
                        tvShow.translationX = it.animatedValue as Float
                    }
                    start()
                }
            }
        }
複製程式碼
  1. 程式碼定義動畫
val animator = ValueAnimator.ofFloat(0f, 1000f)
                .apply {
                    duration = 3000
                    interpolator = AccelerateInterpolator()
                    addUpdateListener {
                        tvShow.translationX = it.animatedValue as Float
                    }
                }
        btnMove.setOnClickListener {
            animator.start()
        }
複製程式碼

上面兩種實現是一樣的效果,耗時3s,transitionX從0變為1000,ValueAnimator需要新增UpdateListener得到實際的屬性值,然後賦值給對應View。

使用ObjectAnimator

效果與上面相同,如下:

ObjectAnimator效果

  1. xml定義動畫
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="3000"
                android:interpolator="@android:anim/accelerate_interpolator"
                android:propertyName="transitionX"
                android:valueFrom="0"
                android:valueTo="1000"
                android:valueType="floatType"
    />
複製程式碼

配合程式碼:

val animatorFromXml = AnimatorInflater.loadAnimator(this, R.animator.object_move_view)
        btnMoveFromXml.setOnClickListener {
            if(animatorFromXml is ValueAnimator){
                animatorFromXml.apply {
                    addUpdateListener {
                        tvShow.translationX=animatedValue as Float
                    }
                    start()
                }
            }
        }
複製程式碼
  1. 程式碼定義動畫
val animator = ObjectAnimator.ofFloat(tvShow,View.TRANSLATION_X,0f,1000f).apply {
            duration=3000
            interpolator=AccelerateInterpolator()
        }
        btnMove.setOnClickListener {
            animator.start()
        }
複製程式碼

與ValueAnimator相比,ObjectAnimator傳入了要運動的View,所以也就不需要在UpdateListener中控制View。

AnimatorSet

如果需要同時開啟多個動畫,那麼可以使用AnimatorSet,串聯組織多個動畫。
先看效果:

AnimatorSet效果

具體效果是首先透明度變化,然後transitionX和transitionY一起變化,最後透明度再變化一波。程式碼如下:

val alphaStartAnim = ObjectAnimator.ofFloat(tvShow, View.ALPHA, 1f, 0f, 1f)
        val xAnim = ObjectAnimator.ofFloat(tvShow, View.TRANSLATION_X, 0f, 1000f)
        val yAnim = ObjectAnimator.ofFloat(tvShow, View.TRANSLATION_Y, 0f, 500f)
        val alphaEndAnim = ObjectAnimator.ofFloat(tvShow, View.ALPHA, 1f, 0f, 1f)
        btnMove.setOnClickListener {
            AnimatorSet().apply {
                play(xAnim).after(alphaStartAnim)
                play(xAnim).with(yAnim)
                play(xAnim).before(alphaEndAnim)
                start()
            }
        }
複製程式碼

總結

至此,我們可以使用ValueAnimator、ObjectAnimator或AnimatorSet進行建立動畫,然後作用於View或其他物件。

關於程式碼,參考Github

參考

關注我的技術公眾號,不定期會有技術文章推送,不敢說優質,但至少是我自己的學習心得。微信掃一掃下方二維碼即可關注:

二維碼

相關文章