Android動畫基礎詳析 | 屬性動畫基礎及ValueAnimator
- 2019 年 10 月 11 日
- 筆記
為什麼要引入屬性動畫
- 逐幀動畫主要是用來實現動畫的, 而補間動畫才能實現控件的漸入漸出、移動、旋轉和縮放效果; 屬性動畫是在Android 3.0時才引入的,之前是沒有的。
既然補間動畫和逐幀動畫已經很全了,為什麼還要引入屬性動畫呢?
- 假設:如何利用補間動畫來將一個控件的背景色在1分鐘內從綠色變為紅色? 這個效果是沒辦法僅僅通過改變控件的漸入漸出、移動、旋轉和縮放來實現的, 但卻可以通過屬性動畫完美地實現。 這就是要引入屬性動畫的第一個原因: 屬性動畫是為了彌補視圖動畫的不足而設計的, 能夠實現補間動畫無法實現的功能。
補間動畫
和逐幀動畫
統稱為視圖動畫
, 從字面意思
中可以看出, 這兩個動畫
只能對派生自View類
的控件實例
起作用; 而屬性動畫
, 從名字中
可看出它是作用於控件屬性
的。 正因為屬性動畫
能夠只針對控件
的某一個屬性
來做動畫, 所以造就了它能單獨
改變控件某一個屬性的值
,比如顏色
。 這就是屬性動畫
能實現補間動畫
無法實現的功能的最重要
的原因。- 視圖動畫僅能對指定的View實例控件做動畫, 而
屬性動畫
是通過改變控件
的某一屬性值
來做動畫的。
我們準備一個button和一個TextView, 首先給TextView控件添加了單擊響應事件, 當單擊該TextView時,會彈出Toast提示; 然後, 在單擊按鈕的時候,TextView控件開始向右下角移動。 從結果中可以看出, 在移動前,單擊TextView控件是可以彈出Toast提示的; 而在移動後,單擊TextView控件則沒有響應, 相反,單擊TextView控件原來所在的區域會彈出Toast提示。 這就說明補間動畫雖然能對控件做動畫, 但是並沒有改變控件內部的屬性值。
視圖動畫與屬性動畫的區別
- 1.操作對象
- 視圖動畫只能操作視圖對象(各種組件、各種View、ViewGroup);
- 屬性動畫可以操作任意對象(除了View,還可以是基本類型數據等);
- 動畫系統本質:給定一個初始值和一個終止值, 令對象從初始值到終止值做一個平滑的變化(變化過程可以變速、勻速、不規則速度)
-
- 屬性的改變
- 視圖動畫沒有對屬性做真正的改變,只是做齣動畫效果而已; (位移動畫後View的響應區沒有改變;縮放動畫結束後獲取View的長寬其值亦沒有改變)
- 屬性動畫能夠做真正的屬性改變;
- 視圖動畫實現的效果,屬性動畫都能實現;
從直觀上來看,視圖動畫與屬性動畫有如下三點不同。 (1)引入時間不同:View Animation是在API Level 1時引入的;而Property Animation是在API Level 11時引入的,即從Android 3.0才開始有與Property Animation相關的API。 (2)所在包名不同:View Animation API在android.view.animation 包中,而Property Animation API在android.animation包中。 (3)動畫類的命名不同:View Animation中的動畫類命名都是XXXXAnimation,而Property Animation中的動畫類命名都是XXXXAnimator。
動畫屬性
- 1 時長
- 2 時間插值器
- 3 重複次數以及重複模式
- 4 動畫集
- 5 延遲
- 屬性動畫乾的事情,就是在一段時間內讓屬性值不斷地做變化; (變化過程可以變速、勻速、不規則速度), 一系列的屬性改變即成就了一個動畫;
- 屬性動畫相關的類, 都被定義在了android.animation包當中, 包中有一個抽象類Animator, 它包含了以上提到的五個屬性的相關方法;
- 動畫對象都是可悲開始、可被暫停、可被監聽的;
- Animator的子類
- ValueAnimator 控制值的變化; 屬性動畫乾的事情,就是在一段時間內讓
屬性值
不斷地做變化; ValueAnimator 就是令這個屬性值
不斷地做變化的驅動;
- ValueAnimator 控制值的變化; 屬性動畫乾的事情,就是在一段時間內讓
ValueAnimator
在上篇博客Android動畫基礎詳析 | 概述、逐幀動畫、視圖動畫(附諸多實際運行效果動圖)的基礎上我們新建一個property包和一個PropertyActivity:

activity_property.xml:
<?xml version="1.0" encoding="utf-8"?> <RealativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".property.PropertyActivity"> <Button android:id="@+id/btnValueAnimator" android:text="Go" android:onClick="onClick" android:layout_width = "match_parent" android:layout_height= "wrap_content"/> </RealativeLayout>
PropertyActivity.java:
public class PropertyActivity extends AppCompatActivity { private static final String TAG = "PropertyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_property); } public void onClick(View view){ switch (view.getId()){ case R.id.btnValueAnimator: ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);//ValueAnimator的正態方法ofInt,驅動整型數值 valueAnimator.setDuration(100);//設置時長 //設置屬性刷新監聽器 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //通過animation對象可以獲取諸多動畫相關屬性 //獲取當前的動畫變化完成度,範圍 0.0 - 1.0,0.0表示剛開始, 1.0表示完成 float animatedFracion = animation.getAnimatedFraction(); //獲取當前狀態基於正態方法的始末參數間的插值,強制轉換的類型就看正態方法的數據類型; int animatedValue = (int)animation.getAnimatedValue(); //打印這兩個參數,其中%.3f即保留小數點後三位 Log.d(TAG, "onAnimationUpdate: " + String.format("%.3f %d", animatedFracion,animatedValue)); } }); valueAnimator.start();//開始動畫 break; } } }
運行代碼:
第一列數據是動畫變化完成度,第二列數據是插值, 我們可以看到打印出來的值並不是線性的,??? 因為ValueAnimator默認的插值器不是勻速的;??? 下面給ValueAnimator設置插值器即可:
... valueAnimator.setDuration(100);//設置時長 valueAnimator.setInterpolator(new LinearInterpolator());//設置插值器 //設置屬性刷新監聽器 ...
ValueAnimator的簡單使用案例
-
ValueAnimator.ofFloat(0f,400f,50f,300f)
構造了一個比較複雜的動畫漸變, 值從0變到400,再回到50,最後變成300, 通過getAnimatedValue()函數來獲取當前運動點的值, 在得到當前運動點的值以後, 通過layout()函數將TextView移動到指定位置即可


- ValueAnimator只負責對指定值區間進行動畫運算; 我們需要對運算過程進行監聽,然後自己對控件執行動畫操作。
- ofInt()與ofFloat()的唯一區別就是傳入的數值類型不一樣,ofInt()函數需要傳入Integer類型的參數,而ofFloat()函數則需要傳入Float類型的參數。 它們的參數類型都是可變長參數,所以我們可以傳入任何數量的值; 傳進去的值列表就表示動畫時的變化範圍, 比如ofInt(2,90,45)就表示從數字2變化到數字90再變化到數字45, 所以我們傳進去的數字越多, 動畫變化就越複雜。
- 為什麼通過getAnimatedValue()函數來獲取當前valueAnimator產生的值的時候,需要轉換成Integer/Float類型?
getAnimatedValue()函數的聲明
Object getAnimatedValue();
它返回的是Object原始類型, 那我們怎麼知道要將它轉換成什麼類型呢? 注意, 如果我們在設定動畫初始值時使用的是ofFloat()
函數, 則每個值的類型必定是Float
類型, 我們獲取到的類型也必然是Float
類型。 同樣,如果我們使用ofInt()
函數設定動畫初始值, 那麼通過getAnimatedValue()
函數獲取到的值 就應該轉換為Integer
類型。
常用函數匯總


- setRepeatCount(int value)函數用於設置動畫循環次數, 設置為0表示不循環, 設置為ValueAnimation.INFINITE表示無限循環。
- cancel()函數用於取消動畫。
- setRepeatMode(int value)函數用於設置循環模式, 當取值為ValueAnimation.RESTART時,表示正序重新開始; 當取值為ValueAnimation.REVERSE時,表示倒序重新開始。
- 注意:重複次數為INFINITE(無限循環)的動畫, 當Activity結束的時候,必須調用cancel()函數取消動畫, 否則動畫將無限循環,從而導致View無法釋放, 進一步導致整個Activity無法釋放,最終引起內存泄漏。
監聽器
- animator.addUpdateListener,用於監聽動畫過程中值的實時變化。 其實在ValueAnimator中,共有兩個監聽器:

- 當動畫開始時,會通過onAnimationStart()函數返回; 在每一次重複時,都會調用一次onAnimationRepeat()函數; 在調用cancel()函數取消動畫時,會通過onAnimationCancel()函數返回; 在動畫終止時,會調用onAnimationEnd()函數通知用戶;
移除監聽器

- removeListener(AnimatorListener listener)函數 用於在Animator中移除指定的監聽器;
- removeAllListeners()函數 用於移除Animator中所有的監聽器。
其他函數

- setStartDelay(long startDelay)函數非常容易理解,就是設置延時多久後動畫開始。
- clone()函數就是複製出來一個完全一樣的新的ValueAnimator實例, 對原來的ValueAnimator是怎麼處理的, 在這個新的實例中也採用相同的處理方式;
至此,補充一個實戰: 自定義View實戰 | 彈跳的loading效果 一個主要是由ValueAnimator實現的自定義View
- 本文內容參考自 慕課網. 就業班
- 以及《Android自定義控件開發入門與實戰》