Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android API Guide for Animation and Graphics(二)—— 動畫與圖形(屬性動畫)

Android API Guide for Animation and Graphics(二)—— 動畫與圖形(屬性動畫)

編輯:關於Android編程

Android API Guide for Animation and Graphics(二)—— 動畫與圖形(屬性動畫)

屬性動畫

屬性動畫系統是一個允許你讓幾乎所有對象都能有動畫的魯棒性架構。你可以定義一個隨時間軸改變對象的屬性,且無需對象是否已繪制好在屏幕上的動畫。屬性動畫在指定時間內改變對象的屬性的值。為了賦予對象動畫,你需要指定一個對象的目標屬性,比如對象在屏幕上的位置,以及它動畫的時長,還有動畫期間屬性變化的值。

屬性動畫系統定義了如下的特性:

Duration:你可以指定動畫的播放時長,默認是300ms。 Time interpolation:時間插值器,你可以指定如何計算屬性的值作為動畫當前已過時間的函數。 Repeat count and behavior:你可以指定動畫結束之後是否重復、重復的次數、是否要反向播放動畫、正/反播放動畫的順序是否重復。 Animator sets:你可以創建動畫集在指定延遲時間後同時播放或者按順序播放。 Frame refresh delay:你可以指定動畫多久進行刷新,默認是10ms。但是你的應用程序動畫的刷新速率取決於你系統的繁忙程度以及系統運行的快慢。

屬性動畫的工作原理

首先,讓我們通過一個簡單的例子來了解屬性動畫的工作原理。圖1假設在屏幕的水平位置上有一個x屬性在進行動畫的對象。動畫時長為40ms以及運行了40px的距離。由於屬性動畫的默認刷新速率為10ms,所以對象每10ms水平移動10px。當到了40ms的時候,動畫就結束了,對象也停留在了水平位置40px處。這個動畫例子是通過一個勻速插值器來實現的。

這裡寫圖片描述
圖1.線程動畫

你也可以通過指定屬性動畫的插值器為非線性的。圖2表明了動畫在開始時加速,在將結束時減速。但對象仍然在40ms中移動了40px。開始時,動畫加速到中點,然後從中點減速到動畫結束。正如圖所示,動畫開始和結束移動的距離比在中間點移動的要少。

這裡寫圖片描述
圖2.非線性動畫

讓我們仔細的看看屬性動畫的重要組件是如何計算如上文的動畫。圖3描述ValueAnimator是如何一步步運行的。

這裡寫圖片描述
圖3.動畫是如何計算的。

ValueAnimator對象通過跟蹤動畫的時間,例如動畫已過了多長的時間,以及當前屬性的值。

ValueAnimator封裝了定義動畫插值規律的時間插值器(TimeInterpolator),以及定義了如何計算屬性的值的類型估值器(TypeEvaluator)。如圖2中,使用了AccelerateDecelerateIntercepolator的時間插值器和IntEvaluator的類型估值器。

要啟動一個動畫,需要創建一個ValueAnimator並賦予其起始和結束的屬性值。當你調用start()的時候動畫就開啟了。在整個動畫期間,ValueAnimator根據動畫播放的總時長和已經過去的時間從0到1計算已過去時間的分數。已過時間的分數表示動畫完成的百分比。0意味著0%,1意味著100%。比如圖1,t = 10ms的時候,過去時間分數為0.25,因為總時長為40ms。

當ValueAnimator計算完過去時間分數的時候,它就會調用當前設置的TimeIntercepolator來計算插值的分數。插值分數將其插值到當前設置的時間插值器上去。比如圖2,因為動畫是緩慢的加速,插值分數約為0.15,比圖1的0.25小。在圖1中的插值分數總是0.25。

當插值分數計算完之後,ValueAnimator調用合適的類型估值器(TypeEvaluator)並根據這個插值分數來計算動畫屬性當前的值,開始的值,結束的值。比如圖2,t = 10ms時插值分數為0.15,所以這個時刻屬性的值就為0.15X(40 - 0) = 6

屬性動畫與視圖動畫的區別

視圖動畫系統只提供作用於View對象的動畫功能,所以如果你想將視圖動畫使用到非View對象上,你不得不自己實現代碼。視圖動畫實際還有其他限制,就是它只暴露了幾種動畫,比如縮放,旋轉,但是它沒有提供像改變背景色值這樣的動畫接口。

視圖動畫系統另一個缺點就是它只在需要繪制視圖的地方進行修改,而不是View本身。比如,如果你動畫一個按鈕在屏幕移動,該按鈕正常繪制了,但事實上按鈕的點擊位置還在原來的位置上,你只能通過自己實現一些代碼來處理這種情況。

如果是使用屬性動畫,這些約束就不存在了,你可以將屬性動畫作用於任何對象的任何屬性上(包括View和非View的對象),而且它是修改對象自身。屬性動畫系統在執行動畫方面也是比較魯棒的。在高水平級別應用上,你可以將動畫賦予到你想要的屬性上,如顏色,位置,或者大小,而且在自定義動畫方面,你可以自定義插值器和多個同步運行的動畫。

然而,視圖動畫只需花費較少的時間以及需要較少的代碼就可以實現。如果視圖動畫能實現你所需,或者你現有的代碼已經實現了你所需,就沒有必要去另外用屬性動畫了。如果有這種需求時,也可以使用兩種動畫系統來處理不同的情況。


API Overview

你可以在android.animation包中找到大部分屬性動畫系統的API。因為視圖動畫系統已經定義了很多插值器在android.view.animation包中,你也可以使用這些插值器。下面的列表是屬性動畫系統主要成員。

Animator類提供了基本的構造函數來創建動畫。通常你不需要直接使用此類,因為繼承它只是實現了最基本的動畫功能。以下是Animator的子類擴展

Table1.Animators

1.1 ValueAnimator

計算賦予動畫的屬性的值的主要時間引擎。它包含了計算屬性動畫的值、獲取每個動畫的時間細節、獲取關於是否動畫重復,是否有監聽器、是否接收更新事件的信息以及自定義估值器的所有核心功能。屬性動畫需要兩個步驟:一就是計算屬性動畫的值,並在賦予動畫的對象上設置這些值。ValueAnimator沒有實現第二部分的細節,所以你必須在你業務代碼中實現監聽並更新屬性的值。

1.2 ObjectAnimaor

ValueAnimator的子類 ,可以作用於對象和對象的屬性上。這個類會在動畫期間計算出一個新的屬性值之後更新屬性。大多數情況會使用這個類,因為它使你程序上的目標對象的屬性值更容易改變。然而,有時候你得直接使用ValueAnimator,因為ObjectAnimator有一些約束,比如需要在目標對象上指定一個可訪問的方法(後面有使用ObjectAnimator的注意點)。

1.3 AnimatorSet

提供一種將動畫組在一起以使它們彼此相關運行的機制。你可以設置這些動畫同時播放,順序播放,或者指定某個延遲時間後播放。


估值器定義屬性動畫系統如何根據指定的屬性計算值。他們攜帶著Animator類提供的時間參數以及動畫的起始值和結束值,並根據這些參數計算動畫的屬性值。屬性動畫系統提供了如下的估值器:

Table2.Evaluator

2.1 IntEvaluator

默認計算整型屬性值的估值器。

2.2 FloatEvaluator

默認計算浮點型屬性值的估值器。

2.3 ArgbEvaluator

默認計算十六進制色值屬性的估值器

2.4 TypeEvaluator

允許你自定義類型的估值器接口。如果你指定的動畫屬性的值不是int,float或者color,你必須通過實現類型估值器來指定如何計算對象屬性的值。如果你想讓默認的類型估計器有所不同,你也可以指定自定義類型估值器為int,float或者color。


時間插值器定義動畫中指定的值如何計算並作為時間的函數。比如,你可以指定整個動畫過程是線性移動的,這意味著這個動畫過程是勻速移動的,或者你可以指定動畫使用非線性時間插值器,打個比方,開始動畫時加速,結束動畫時減速。表格3描述android.view.animation包中的插值器。如果沒有一個適合你的插值器類型,你可以通過實現TimeInterpolator接口定義自己的插值器。

Table3.Interpolators

3.1 AccelerateDecelerateInterpolator

起始和結束的速率是緩慢變化的,中間速率是加速的插值器。

3.2 AccelerateInterpolator

緩慢開始然後加速的插值器。

3.3 AnticipateInterpolator

An interpolator whose change starts backward then flings forward.

3.3 AnticipateOvershootInterpolator

An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.

3.4 BounceInterpolator

在結束時反彈的插值器。

3.5 CycleInterpolator

動畫指定重復次數周期插值器。

3.6 DecelerateInterpolator

快速開始然後減速的插值器。

3.7 LinearInterpolator

速率勻速變化的插值器。

3.8 OvershootInterpolator

An interpolator whose change flings forward and overshoots the last value then comes back.

3.9 TimeInterpolator

允許你自定義插值器的接口。


Animating with ValueAnimator

ValueAnimator類通過指定int,float或者color,並在動畫期間設置這些屬性的值。你可以通過調用ValueAnimator的工廠方法:ofInt(),ofFloat(),ofObject(),如下:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();

這段代碼,當start()方法被調用的時候,ValueAnimator在1000ms內從0到1計算動畫的值。
你也可以像下面一樣指定一個自定義類型來播放動畫:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

這段代碼中,當start()方法調用時,ValueAnimato就根據MyTypeEvaluator提供的邏輯計算startProperty和endPropertyValue之間的動畫值,持續時間為1000ms。

然而,前面的代碼片段對對象是沒有任何實際的影響的。因為ValueAnimator不能直接操作一個對象或者對象的屬性,更多的是獲取計算的值來操作修改對象。你需要實現ValueAnimator的監聽器來適當的處理動畫期間的變化,比如動畫幀的更新。當實現好監聽器的時候,你可以通過調用getAnimatedValue()獲取屬性動畫計算的值.


Animating with ObjectAnimator

ObjectAnimator是ValueAnimator的子類(在前面的部分中討論),它結合了定時引擎和ValueAnimator的值計算以及對對象的屬性進行動畫的處理能力。它可以讓任何對象實現屬性動畫更加簡便,因為它自動幫你實現ValueAnimator.AnimatorUpdateListener的接口,你無需再自己去實現。

ObjectAnimator的使用示例很像ValueAnimator,但是你要指定作用的對象以及對象跟隨動畫播放的時間改變的屬性(用字符串形式表示,如下代碼的“alpha”)

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

想要ObjectAnimator正確的更新屬性值,你必須遵循以下幾點:

賦予屬性動畫的對象必須有一個如set()的setter方法。因為ObjectAnimator在動畫期間自動更新屬性的值,它必須有能夠訪問該屬性的setter方法。比如,如果屬性名是foo,你就需要提供一個setFoo()方法。當不存在setter方法時,你有三種解決方法可以選擇:

在有權限的條件下,添加setter方法到類中 使用有setter方法接收計算的值並轉發到原始對象的包裝類中。 使用ValueAnimator替代

如果OjectAnimator的工廠方法中values…參數只傳入一個指定的值,這個值將被視作動畫結束時的值。因此,賦予屬性動畫的對象必須提供一個格式如get的getter方法來獲取動畫屬性的起始值。比如,屬性名為foo,則需要提供一個getFoo()方法。

對象暴露的getter方法(在需要提供的情況下)和setter方法在動畫期間處理的類型,必須與指定在ObjectAnimator的起始值和結束值的類型一樣。比如,如果你用下面的參數值構造ObjectAnimator(結束值為1f,浮點類型),你必須提供targetObject.setPropName(float)和targetObject.getPropName(float)

ObjectAnimator.ofFloat(targetObject, "propName", 1f)
取決於動畫的屬性和對象的類型,你可能需要在View中調用invalidate()方法並根據屬性動畫更新的值來強制屏幕重繪View本身。這個情況需要在onAnimationUpdate()方法中回調。比如,賦予Drawable對象色值屬性動畫後,它只會在對象重繪自身的時候才會去更新屏幕自身的狀態。所有View的屬性設置,比如setAlpha(),setTranslationX()都能正確的invalidate,所以當有新的值傳入這些設置器的時候,你不需要再調用invalidate View。

通過AnimatorSet組合多個動畫

在許多情況,你播放的動畫都是依賴於另一個動畫的開始或者結束。Android系統允許你將多個動畫綁定到AnimatorSet中,你可以指定是否同時播放動畫,順序播放,或者某個延遲時間後開始。你也可以在AnimatorSet中再內嵌一個AnimatorSet動畫集。

下面通過Bouncing Balls的動畫播放順序來演示AnimatorSet的使用方式:
1. 播放 bounceAnim.
2. 同時播放squashAnim1, squashAnim2, stretchAnim1, and 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();

Animatin Listeners

你可以在動畫期間設置的監聽器:

Animator.AnimatorListener
onAnimationStart() - 動畫開始時調用 onAnimationEnd() - 動畫結束時調用 onAnimationRepeat() - 動畫重復時調用 onAnimationCancel() - 動畫被取消時調用,且不管動畫是否已經結束也會調用onAnimationEnd() ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() – 動畫期間每一幀都會調用一次(默認10ms刷新一次),監聽這個事情可以獲取ValueAnimator在動畫期間計算的值。想要獲取這個值,需要調用通過ValueAnimator對象的getAnimatedValue()方法並傳到這個事件中去。

如果你不想實現Animator.AnimatorListener所有的方法的話,你可以通過繼承AnimatorListenerAdapter來代替實現Animator.AnimatorListener接口,AnimatorListenerAdapter類提供了幾個空的方法實現給你選擇並重寫。

如下實例:Bouncing Balls Demo創建AnimatorListenerAdapter只是想回調onAnimationEnd()方法。

ValueAnimator 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的布局上的變化

屬性動畫系統提供了對ViewGroup對象進行動畫變更的功能,以及對View對象更容易進行動畫變更的方式。
你可以在ViewGroup中使用LayoutTransition類實現的動畫對布局進行改變。ViewGroup也可以給裡面View的顯示與隱藏及添加與刪除添加動畫。這意味著當添加View或者刪除View的時候,可以通過動畫設置它們新的位置。你可以在LayoutTransition對象中通過調用setAnimator()定義動畫,並將該動畫傳入賦值到如下LayoutTransition的一個常量中:

APPEARING - items出現在容器中時運行動畫的標記。 CHANGE_APPEARING - items由於一個新的item的出現在容器中而變更時運行動畫的標識。 DISAPPEARING - items退出容器中時運行動畫的標記。 CHANGE_DISAPPEARING - items由於一個item退出容器而變更時運行的動畫標識。
你可以為這四種事件類型自定義動畫來定制你布局過度的樣式,或者直接使用默認的動畫。

LayoutAnimations的示例教你如何給定義布局過渡動畫,並設置到想要賦予動畫效果的View對象上。
PS:LayoutAnimation這裡沒有過多介紹,Google走起


使用類型估值器

如果你想對一個Android系統中沒有的類型進行動畫,你可以通過實現TypeEvaluator接口創建自己的類型估值器。IntEvaluator,FloatEvaluator、ArgbEvaluator類型估值器支持Andorid系統已知幾種類型:int、float、color。

實現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);
    }
}

Note:當ValueAnimator或者ObjectAnimator動畫在運行時,它計算出當前已過時間的分數(0到1)然後根據你當前的插值器類型轉換插值數。這個插值數就是你類型估值器接收的參數(如代碼中的float fraction),所以當動畫的值在計算的時候,你不需要再考慮這個插值數的計算。


使用插值器

插值器定義動畫如何根據指定的值計算出作為時間函數的值。例如,你可以指定整個動畫期間線性播放,這意味著整個動畫過程是勻速的,或者你可以指定動畫為非線性移動,比如在開始時加速或在結束時加速。

在動畫系統中插值器接收一個代表動畫已過時間的分數。插值器根據動畫所提供的類型來修改這個分數(不理解可以看下文幾種插值器的getIntercepolation的實現)。Android系統的android.view.animation.package包提供了一系列常用的插值器,如果沒有一個適合你的的話,你也可以通過實現TimeIntercepolator接口創建自己的插值器。

舉個例子,比較 AccelerateDecelerateInterpolator和 LinearInterpolator是如何計算插值的分數的。已過時間分數對LinearInterpolator沒有影響。AccelerateDecelerateInterpolator在動畫載入時加速,在動畫載出時減速。下面的方法是這些插值的計算邏輯:

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

指定關鍵幀

一個關鍵幀由時間/值 對組成,它定義動畫指定的時間出現指定的狀態。每個關鍵幀也可以有它自己的插值器來控制動畫前一個關鍵幀與當前關鍵幀的動畫之間那段間隔的動畫效果。

初始化一個關鍵幀對象,你必須使用一個工廠方法,ofInt(),ofFloat(),ofObject()來獲取合適的關鍵幀類型。然後通過調用工廠方法ofKeyframe()來獲取PropertyValueHolder對象。一旦你獲取該對象,你就可以通過該對象獲取一個動畫,並賦予到你要進行動畫效果的對象上。下面的代碼片段是Keyframe的使用示例:

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

屬性動畫實現的視圖動畫

屬性動畫系統可以實現流線型的View對象動畫,它的優點比View Animation的多。視圖動畫系統通過改變View的繪制方式來切換View對象。這只能在每個View的容器內處理,因為View本身沒有可操作的屬性。這將導致雖然視圖在變換,但是View本身是沒有改變的。在Android 3.0,新的屬性以及相應的getter和setter方法來消除這個缺點。

屬性動畫系統可以通過真正的改變View對象的屬性來實現視圖動畫效果。另外,Views也會隨著屬性值的改變自動的調用invalidate()方法來刷新。View中可以用於屬性動畫的新屬性是:

translationX and translationY:這些屬性控制視圖作為從其布局容器設置的左側和頂部坐標的增量的位置 rotation, rotationX, and rotationY:這些屬性控制2D旋轉(rotation)和圍繞樞紐點進行的3D旋轉 scaleX and scaleY:控制View圍繞樞紐點2D的縮放 pivotX and pivotY:動畫圍繞的樞紐點,默認是對象的中心點。 x and y:用於在其容器中描述視圖的最終位置,其中X作為左邊距+translationX的和,Y表示上邊距+translationY的和。 alpha: 控制透明度

使用示例:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

使用ViewPropertyAnimatoer進行動畫

ViewPropertyAnimator提供一個簡單的方法來實現View的幾種屬性動畫。它更像是ObjectAnimator,因為它是通過修改View本身的屬性值,但是通過它實現屬性動畫效率更高。另外,ViewPropertyAnimator的代碼也非常簡潔跟易讀。下面通過同時改變View的x,y屬性代碼片段用來展示使用多個ObjectAnimator對象、單獨的ObjectAnimator對象以及ViewPropertyAnimator的區別:
Multiple ObjectAnimator objects

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

One ObjectAnimator

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

ViewPropertyAnimator

myView.animate().x(50f).y(100f);

XML中聲明動畫

屬性動畫系統可以讓在xml資源文件中聲明動畫。通過在XML中定義動畫,你可以更容易的修改動畫,且在多個Activity中重復使用。

為了從遺留下來的視圖動畫中區分出屬性動畫資源文件,從Android 3.1開始,你需要保存XML文件在res/animator目錄下。

下面是在XML中聲明屬性動畫的標簽:

ValueAnimator - [animator] ObjectAnimator - [objectAnimator] AnimatorSet - [set]

下面的示例順序的播放兩個動畫對象集,其中第一個對象集又內嵌了兩個動畫對象:


    
        
    

為了運行這個動畫,你需要將XML資源文件填充到代碼中的AnimatorSet對象中,然後在開始執行動畫之前將所有的動畫設置到目標對象上。通過調用setTarget()可以很方便的把動畫集的所有動畫設置到目標對象上。下面是示例代碼:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved