屬性動畫不僅僅能作用於View,而能作用於任何物件。
與ViewAnimation的區別
ViewAnimation只支援幾種動畫:scale、transition、rotate、alpha四種型別。並且缺陷是隻是改變了顯示位置,實際位置並沒有改變。
一個demo解釋一切,如下圖:
Hello按鈕使用ViewAnimation進行移動,點選事件在移動後,但是響應還在最初的位置;而屬性動畫移動的World按鈕則不同,響應是跟著按鈕走的。
屬性動畫原理
屬性動畫的一些引數
建立一個屬性動畫,一般需要設定幾個引數,如下:
- duration:動畫的持續時長,預設300ms
- Time interpolation:時間插值器,是一個函式,property=f(time),隨著時間計算屬性的函式
- 重複次數和行為:可以指定動畫是否重複,以及重複次數;也可以指定動畫是否reverse
- AnimatorSet:可以組合多個動畫,同時作用or分批作用
- 幀重新整理延遲:可以指定多久重新整理一幀動畫,預設值是每10s重新整理一幀,但實際上的值還是要依賴於系統的實際執行情況。
原理說明
以官方圖為例:
一個動畫,40ms,從左向右移動40pixel,每隔10ms,新的幀被畫出來了,動畫停止時,View停在了最終位置。
上面的例子是一個線性的效果,時間插值器的函式是:
pixel=1*time
複製程式碼
下面是一個非線性的例子,如圖:
這個只需要改變插值器即可,可以看到先加速到一半,再開始減速。
下面開始正式說明屬性動畫的原理,首先看下圖:
可以看到核心是ValueAnimator這個類會追蹤動畫的時長,當前屬性值。
ValueAnimator封裝了TimeInterpolation和TypeEvaluator,TimeInterpolation用來計算每一幀對應時間的屬性,TypeEvaluator用來從動畫中獲取屬性值。
建立一個動畫並開啟後,屬性動畫主要有三步操作:
- 根據時間流失,得到一個已過時間因子,這個值的範圍是[0,1],以上面的例子為例,總時長40ms,而每一幀10ms,第一幀的已過時間因子就是0.25
- 得到已過時間因子後,ValueAnimator會呼叫TimeInterpolation計算得到屬性因子,以上面的例子為例,已過時間因子是0.25,線性插值器的計算結果也是0.25
- 得到了插值器的結果後,ValueAnimator會呼叫TypeEvaluator計算具體的屬性值,然後改變View的該屬性值。
每一幀,經過這麼計算,就是屬性動畫的原理。
關於API
主要是ValueAnimator,ObjectAnimator,AnimatorSet,類結構圖如下所示:
使用
Animator和Animation一樣,既可以程式碼實現,也可以在xml中定義,下面分別說明兩種方式分別是如何操作的。
使用ValueAnimator
ValueAnimator有一些靜態方法,ofInt、ofFloat、ofObject。
先看效果,
- 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()
}
}
}
複製程式碼
- 程式碼定義動畫
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
效果與上面相同,如下:
- 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()
}
}
}
複製程式碼
- 程式碼定義動畫
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,串聯組織多個動畫。
先看效果:
具體效果是首先透明度變化,然後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
參考
關注我的技術公眾號,不定期會有技術文章推送,不敢說優質,但至少是我自己的學習心得。微信掃一掃下方二維碼即可關注: