標籤:android 屬性動畫 動畫
property 動畫系統是相當健壯的架構,它幾乎可以動畫顯示任何對象。 你可以定義一個動畫來定時改變任何對象的屬性值,不論該對象是否在螢幕上顯示。 property 動畫將以一定的時間間隔修改屬性值(對象中的欄位值)。 要實現動畫顯示,你須指定對象的相應屬性(比如對象的螢幕位置),以及動畫時間長度、動畫時間間隔。
property 動畫系統能讓你設定以下動畫要素:
1.期間:指定動畫的持續顯示時間。預設的時間長度是300毫秒。
2.插值因子:指定屬性值的變化方式,表示為關於動畫已顯示時間的函數。
3.重複次數和方式:指定動畫是否迴圈播放,以及重複的次數。還可以指定動畫是否反向播放。可以設為先正向播放4.再反向回放,如此往複直至達到設定的重複次數。
5.動畫集合:你可以把動畫分為多個邏輯組,以便實現同時播放、順序播放或間隔一段時間再播放。
6.幀重新整理間隔:指定動畫幀的重新整理頻率。預設是每 10 ms重新整理一次,但應用程式實際可執行檔重新整理頻率取決於系統整體的繁忙程度,以及系統對定時器的支援程度。
一、Property 動畫的工作方式
首先,我們通過一個簡單例子來回顧一下動畫的工作原理。圖 1 表明了某對象的 x 屬性變化情況,即在螢幕上水平的位置。 動畫的期間設為 40 ms,移動的距離是 40 個像素點。每隔 10 ms,這是預設的幀重新整理率,此對象橫向移動 10 個像素點。 在 40 ms 到期後,動畫停止,此對象位置橫移 40 個像素點。以下是採用線性插值的樣本,也即此對象以固定的速度移動。
圖 1. 線性動畫的樣本
你還可以把動畫設定為非線性插值方式。圖 2 表示,某對象開始時加速移動,結束時減速移動。 此對象仍然是在 40 ms 內移動 40 個像素點,但是速度是非線性變化的。在開始時,此動畫加速移動至中間位置,然後再減速移動至終點。 2 所示,開始和結束階段移動的距離比中間位置要少一些。
圖 2. 非線性動畫樣本
讓我們來仔細查看一下 property 動畫系統的關鍵組件在上述動畫中的計算過程。圖 3 展示了主要類的相互工作方式。
圖 3. 動畫的計算過程
1、ValueAnimator 對象記錄了動畫自身的一些值,比如已經顯示了多長時間、動畫對應屬性的當前值。
2、ValueAnimator 中封裝了一個定義動畫變化方式的 TimeInterpolator ,以及定義了屬性計算方式的 TypeEvaluator 。比如,圖 2 中的 TimeInterpolator 應該使用 AccelerateDecelerateInterpolator , TypeEvaluator 應該使用 IntEvaluator。要啟動動畫,請建立一個 ValueAnimator 並指定要動畫顯示的屬性的初始值和結束值。調用 start() 啟動動畫。在整個動畫過程中, ValueAnimator 根據動畫總時間和已進行的時間和計算出一個時間比例因素(elapsed fraction),大小介於0和1之間。 時間比例因素代表動畫已完成時間的百分比,0 表示 0%,1 表示 100%。例如,在圖 1 中,t = 10 ms 時的時間比例因素應該是 0.25,因為總時間 t = 40 ms。
3、ValueAnimator 算完時間比例因素後,將調用已設定好的 TimeInterpolator 計算出一個插值因子(interpolated fraction)。插值因子是一個由時間比例因素換算出來的映像顯示狀態因子。 比如,在圖 2 中,t = 10 ms 時,因為動畫的加速度較小,插值因子約是 0.15,它小於時間比例因素 0.25。 在圖 1 中,插值因子一直保持不變,並與時間比例因素一致。算完插值因子, ValueAnimator 就會調用合適的 TypeEvaluator ,根據插值因子、初始值、結束值計算出需要動畫顯示的屬性值。 比如,在圖 2 中,t = 10 ms 時的插值因子是 0.15,所以此時的屬性值應該是 0.15 X (40 - 0),也即為 6。
API 概述
你可以在 android.animation 中找到大部分 property 動畫系統的 API。 由於 view 動畫系統已經在 android.view.animation 中定義了很多 interpolator ,你可以在 property 動畫系統中直接使用它們。 下表對 property 動畫系統中的主要內容進行了說明。
Animator 類只提供了用於建立動畫的基礎構造方法。它只提供了最基本的功能,你必須擴充這些功能才能完成對動畫的支援,因此通常你不應該直接使用該類。 以下子類就對 Animator 進行了擴充:
表 1. Animator
evaluator 用於告知 property 動畫系統給定屬性值的計算方式。根據 Animator 類給出的計時資料、動畫初始值和結束值,它將計算出需動畫顯示的屬性值。property 動畫系統提供了以下 evaluator:
表 2. Evaluator
時間 interpolator 定義了動畫顯示的計算方式,它是一個關於時間的函數。 比如,你可以指定線性播放動畫,也即全程勻速播放。或者,你也可以指定為非線性播放,比如一開始加速,而臨近結束時減速。 表 3 列出了 android.view.animation 中包含的 Interpolator。 如果已有的 interpolator 無法滿足需求,你可以實現 TimeInterpolator 介面並建立你自己的 interpolator 。 關於編寫自訂 interpolator 的方法。
表 3. Interpolator
二、用ValueAnimator實現動畫
通過指定一組int、float、顏色值, ValueAnimator 類能讓你在動畫過程中修改某些類型的值。通過調用其Factory 方法(factory method): ofInt()、 ofFloat()、 ofObject(), 你可以獲得一個 ValueAnimator 執行個體。比如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);animation.setDuration(1000);animation.start();
在這段代碼中, ValueAnimator 先設定動畫值為0-1之間、持續 1000 ms、並執行start()方法。
你也可以按照以下格式指定自訂類型的動畫值:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);animation.setDuration(1000);animation.start();
在這段代碼中, ValueAnimator 先計算動畫值為startPropertyValue 至 endPropertyValue之間,應用 MyTypeEvaluator,期間 1000 ms,並執行 start()。
其實這段代碼對某對象是無法實現動畫效果的,因為 ValueAnimator 還沒有直接控制對象或其屬性值。你最有可能要做的事就是用計算出來的屬性值修改動畫對象。 通過在 ValueAnimator 中定義接聽程式,你就可以實現這一點。接聽程式可以實現動畫過程中的事件處理,比如幀的重新整理。 在接聽程式的實現代碼中,你可以調用 getAnimatedValue() 擷取為本次畫面重新整理計算出來的屬性值。
三、用ObjectAnimator實現動畫
ObjectAnimator 是 ValueAnimator的子類,它是由計時引擎和計算目標對象的屬性值 ValueAnimator 組合而成的。因為屬性值將會自動更新,你不再需要實現 ValueAnimator.AnimatorUpdateListener了,因此實現任意對象的動畫顯示就更加容易了。
ObjectAnimator 的樣本與 ValueAnimator 類似,只是你還需要在動畫區間值之前額外指定對象和屬性名稱(字串格式):
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);anim.setDuration(1000);anim.start();
為了保證 ObjectAnimator 能夠正確地更新屬性值,你必須做到以下幾點:
1、動畫顯示的屬性必須帶有一個 setter 方法(以駱駝拼字法命名),格式類似 set<propertyName>()。因為ObjectAnimator會在動畫期間自動更新屬性值,它必須能夠用此 setter 方法訪問到該屬性。例如:假設屬性名稱為foo,則需要有一個setFoo()方法。如果此 setter 方法不存在,你有以下三種選擇:
1>如果你有許可權的話,直接在類中增加此 setter 方法。
2>用你有權修改的封裝類來增加此 setter 方法,並讓該封裝類來接收屬性值並傳給初始的對象。
3>換用 ValueAnimator。
2、如果在調用ObjectAnimator的某個Factory 方法時,你只為 values... 參數指定了一個值,那此值將被認定為動畫屬性的結束值。這樣的話,動畫顯示的屬性必須帶有一個 getter 方法,用於擷取動畫的起始值。 此 getter 方法必須以get<propertyName>()的格式命名。例如:假設屬性名稱為foo,則需要有一個getFoo()方法。
3、動畫屬性的 getter 方法(如果必要的話)和 setter 方法所操作資料的類型必須與ObjectAnimator中設定的起始和結束值相同。比如,如果你建立了以下ObjectAnimator ,那麼就必須有targetObject.setPropName(float) 和 targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
4、根據不同的動畫顯示對象和屬性,也許你需要調用 View 的invalidate()方法,以便用新的屬性值強制重繪螢幕。你應該在onAnimationUpdate() 回調方法中進行重新整理操作。比如,要動畫顯示 Drawable 對象的顏色屬性,就只有通過重繪才能在螢幕上顯示出變化。View 中所有的屬性 setter 方法,比如setAlpha()和setTranslationX(),都會適時地禁用 View,因此你不需要在用新屬性值調用這些方法時禁用 View。
四、用AnimatorSet編排多個動畫
很多時候,你需要在一個動畫的開始和結束時播放另一個動畫。Android 系統可以讓你把多個動畫組合為一個 AnimatorSet ,這樣你就可以設定同時播放、順序播放或延遲一段時間後再播放。 你還可以嵌套多個 AnimatorSet 對象。
如下面範例,將用以下規則播放 Animator 對象:
1、播放 bounceAnim。
2、同時播放 squashAnim1、squashAnim2、stretchAnim1 和 stretchAnim2。
3、播放 bounceBackAnim。
4、播放 fadeAnim。
AnimatorSet bouncer = new AnimatorSet();bouncer.play(bounceAnim).before(squashAnim1);bouncer.play(squashAnim1).with(squashAnim2);bouncer.play(squashAnim1).with(stretchAnim1);bouncer.play(squashAnim1).with(stretchAnim2);bouncer.play(bounceBackAnim).after(stretchAnim2);ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);fadeAnim.setDuration(250);AnimatorSet animatorSet = new AnimatorSet();animatorSet.play(bouncer).before(fadeAnim);animatorSet.start();
五、動畫接聽程式
在播放過程中,你可以用下列接聽程式來監聽那些重要的動畫事件。
Animator.AnimatorListener
onAnimationStart() —— 動畫開始時調用。
onAnimationEnd() —— 動畫結束時調用。
onAnimationRepeat() —— 動畫迴圈播放時調用。
onAnimationCancel() —— 動畫被取消時調用。不管終止的方式如何,被取消的動畫仍然會調用onAnimationEnd()。
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() —— 動畫每播放一幀時調用。在動畫過程中,可偵聽此事件來擷取並使用ValueAnimator計算出來的屬性值。利用傳入事件的ValueAnimator對象,調用其getAnimatedValue()方法即可擷取當前的屬性值。如果你使用ValueAnimator,則必需實現此接聽程式。
根據不同的動畫對象及其屬性,你也許需要調用 View 的invalidate()方法,強制用新的屬性值重繪螢幕地區。比如,要動畫顯示 Drawable 對象的顏色屬性,只有重繪才能在螢幕上顯示出變化。View 中所有的屬性 setter 方法,比如setAlpha()和setTranslationX(),都會適時地禁用 View,因此你不需要在用新屬性值調用這些方法時禁用 View。
如果你不願意實現 Animator.AnimatorListener 中所有的方法,那你可以不用此接聽程式,而是擴充 AnimatorListenerAdapter 類。 AnimatorListenerAdapter 類為所有方法都實現了空方法體,你自行選擇並覆蓋即可。
例如 下面範例 建立了一個只帶有 onAnimationEnd() 回調方法的 AnimatorListenerAdapter
ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);fadeAnim.setDuration(250);fadeAnim.addListener(new AnimatorListenerAdapter() {public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget());}
六、ViewGroup中Layout的動畫
與 View 動畫一樣簡單, property 動畫系統對 ViewGroup 對象的動畫顯示提供了支援。
你可以用 LayoutTransition 類來動畫顯示 ViewGroup 中的 Layout。 通過把 View 加入或移出 ViewGroup ,或者以 VISIBLE、 INVISIBLE、 GONE 調用 View 的 setVisibility() 方法,可以實現某個 View 的顯現和消失效果。 當你添加或刪除 View 時,ViewGroup 中的其它 View 也可以用動畫的方式移到新的位置顯示。 你可以調用 LayoutTransition 對象的 setAnimator() 方法來定義下列動畫方式,調用參數是 Animator 對象和以下 LayoutTransition 常量:
APPEARING —— 元素在容器中顯現時需要動畫顯示。
CHANGE_APPEARING —— 由於容器中要顯現一個新的元素,其它元素的變化需要動畫顯示。
DISAPPEARING —— 元素在容器中消失時需要動畫顯示。
CHANGE_DISAPPEARING —— 由於容器中某個元素要消失,其它元素的變化需要動畫顯示。
你可以為這四種事件定義自己的動畫方式,以便定製 layout 變化時的外觀,也可以只是通知動畫系統採用預設的動畫方式。
範例代碼:
1、需要把 ViewGroup 的 android:animateLayoutchanges 屬性設為 true。 比如:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
此屬性設為 true 將會自動把 View 加入和移出 ViewGroup 的過程以動畫方式顯示,ViewGroup 中其它的 View 同時也會以動畫方式進行調整。
2、為ViewGroup設定Layout的改變動畫
LayoutTransition mTransition = new LayoutTransition();mTransition.setAnimator( LayoutTransition.APPEARING, ObjectAnimator.ofFloat(this, "scaleX", 0, 1)); mGridLayout.setLayoutTransition(mTransition);
PS:更詳細的案例可以參考 鴻洋大神的一篇部落格:
http://blog.csdn.net/lmj623565791/article/details/38092093
七、使用TypeEvaluator
如果 Android 系統無法識別需要動畫顯示的屬性值類型,你可以建立自己的 evaluator,實現 TypeEvaluator 即可。Android 系統可識別的類型包括int、float和顏色,分別由 IntEvaluator、 FloatEvaluator、 ArgbEvaluator 提供支援。
TypeEvaluator介面中只要實現一個方法即可,就是 evaluate()。此方法返回一個當前動畫時刻的屬性值。FloatEvaluator類展示了這一過程:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); }}注意:
當ValueAnimator (或 ObjectAnimator )運行時,它會先算出當前已顯示的動畫進程比例(介於 0 和 1 之間),然後再根據所用的 interpolator 計算出插值因子。 TypeEvaluator 接收到的 fraction 參數本身就是此插值因子,因此你在計算動畫屬性值時就不必用到 interpolator 了。
八、使用Interpolator
插值器(interpolator)定義了動畫過程中屬性值的變化規則,它是一個關於時間的函數。 比如,你可以把動畫過程設定為線性變化,這意味著它全程都是勻速變化。 或者,你可以設定動畫為非線性變化,比如在開始或結束時加速或減速。
動畫系統中的 interpolator 會從 Animator 中接收到一個時間比例因素,此因子代表了動畫已顯示的時間。 interpolator 會根據動畫的類型修改此因子。Android 系統在 android.view.animation 包中提供了一組通用的 interpolator。如果這些都不滿足需要,你可以實現 TimeInterpolator 介面來建立自己的 interpolator。
作為樣本,預設的 AccelerateDecelerateInterpolator 和 LinearInterpolator interpolator 將會以下列方式計算插值因子:
AccelerateDecelerateInterpolator
public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}LinearInterpolator
public float getInterpolation(float input) { return input;}
九、設定主要畫面格
Keyframe 對象中包含了一個時間/屬性值的鍵值對,用於定義某個時刻的動畫狀態。 每個主要畫面格還可以擁有自己的 interpolator,用於控制前一主要畫面格至本主要畫面格之間的動畫行為。
要執行個體化一個 Keyframe 對象,你必須使用它的Factory 方法 ofInt()、 ofFloat()、 ofObject() 之一,或者獲得相應類型的 Keyframe 。然後,調用Factory 方法 ofKeyframe() 獲得一個 PropertyValuesHolder 對象。一旦有了這個對象,你就可以把它和需動畫顯示的對象作為參數得到一個 animator。以下程式碼片段展示了這一過程:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);Keyframe kf2 = Keyframe.ofFloat(1f, 0f);PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)rotationAnim.setDuration(5000ms);rotationAnim.start();
十、動畫顯示View
property 動畫系統可以讓 View 對象進行一系列的動畫顯示,相比 view 動畫系統具有更多優勢。 view 動畫系統通過改變 View 對象的繪製方式來實現動畫效果。 因為 View 本身沒有給出屬性以供控制,所以這是由 View 所在容器來完成處理的。 雖然這樣能實現 View 的動畫效果,但 View 對象本身並沒有變化。 因此會出現這種情況:雖然螢幕上的顯示位置已經移動過了,但對象實際仍然停留在原來的位置。 為了消除這一弊病,在 Android 3.0 中給 View 增加了一些新的屬性以及相應的 getter、setter 方法。
1> property 動畫系統可以通過修改 View 對象實際的屬性值來實現螢幕上的動畫效果。此外,當屬性值發生變化時,Views 也會自動調用 invalidate()方法來重新整理螢幕。 View 類中新增的便於實現 property 動畫的屬性包括:
2> translationX 和 translationY:這兩個屬性控制著 View 的螢幕位置座標變化量,以 layout 容器的左上方為座標原點。
3> rotation、rotationX 和 rotationY:這三個屬性控制著 2D 旋轉角度(rotation屬性)和圍繞某樞軸點的 3D 旋轉角度。
4> scaleX、scaleY:這兩個屬性控制著 View 圍繞某樞軸點的 2D 縮放比例。
5> pivotX 和 pivotY: 這兩個屬性控制著樞軸點的位置,前述的旋轉和縮放都是以此點為中心展開的。預設的樞軸點是 View 對象的中心點。
注意: 1、通過調用mView.setPivotX() 和 mView.setPivotY()來設定某View的旋轉/縮放樞軸點座標。
2、此時的原點座標(0,0)為該旋轉/縮放View的左上方,而不再以手機螢幕左上方為x、y軸座標原點。
6> x 和 y:這是指 View 在容器內的最終位置,等於 View 左上方相對於容器的座標加上 translationX 和 translationY 後的值。
7> alpha:表示 View 的 alpha 透明度。預設值為 1 (不透明),為 0 則表示完全透明(看不見)。
要動畫顯示 View 對象的某個屬性,比如顏色或旋轉值,你所有要做的事情就是建立一個 property animator,並設定對應的 View 屬性。比如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
十一、用ViewPropertyAnimator實現動畫
利用一個基礎的 Animator 對象, ViewPropertyAnimator 為同時動畫顯示 View 的多個屬性提供了一種捷徑。這種方式與一個 ObjectAnimator 很類似,因為也會修改 View 的實際屬性值,但在需要一次動畫顯示多個屬性時效率會更高一些。 此外,使用 ViewPropertyAnimator 的代碼會簡潔很多,可讀性更好。以下程式碼片段展示了分別使用多個 ObjectAnimator 對象、單個 ObjectAnimator 對象、 ViewPropertyAnimator 對象實現 view 的 x 和 y 屬性的同時變化。
多個 ObjectAnimator 對象
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);AnimatorSet animSetXY = new AnimatorSet();animSetXY.playTogether(animX, animY);animSetXY.start();
單個 ObjectAnimator
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();ViewPropertyAnimatorViewPropertyAnimator
myView.animate().x(50f).y(100f);
十二、在XML中聲明動畫
property 動畫系統允許你用 XML 聲明 property 動畫,而不需要編寫代碼來實現。 通過 XML 定義的方式,你可以方便地在多個 activity 中複用動畫資源,並且更容易編排動畫順序。
為了把採用新增 property 動畫 API與採用以前 view animation 架構的動畫檔案區分開來,自 Android 3.1 開始,你應該把 property 動畫 XML 檔案儲存到res/animator/目錄下(而不是res/anim/目錄)。 目錄名animator是可以修改的,但如果你要用 Eclipse ADT(ADT 11.0.0+) 外掛程式作為 layout 編輯工具,那就不能動了。 因為 ADT 只會搜尋res/animator/目錄下的動畫資源。
以下列出了可用 XML 標記聲明的 property 動畫類:
ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet - <set>
以下例子順序播放兩組動畫,第一組動畫內嵌入了兩個對象的動畫:
<set android:ordering="sequentially"> <set> <objectAnimator android:propertyName="x" android:duration="500" android:valueTo="400" android:valueType="intType"/> <objectAnimator android:propertyName="y" android:duration="500" android:valueTo="300" android:valueType="intType"/> </set> <objectAnimator android:propertyName="alpha" android:duration="500" android:valueTo="1f"/></set>
為了播放這個動畫,你必須用代碼把 XML 資源置入 AnimatorSet 中,然後設定動畫的所有目標對象,再開始動畫。用 setTarget() 可以很方便地為 AnimatorSet 下所有子項目設定一個目標對象。以下代碼展示了這種設定方法:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);set.setTarget(myObject);set.start();
備忘:該篇內容轉載於:http://blog.sina.com.cn/s/blog_48d4913001011a1u.html [獃獃大蝦:API翻譯系列]
英文原文:http://developer.android.com/guide/topics/graphics/prop-animation.html
Android學習之 屬性動畫<Property Animation>