Android屬性動畫---Property Animation(二)

用ValueAnimator來製作動畫

ValueAnimator 類通過設定動畫過程中的int、float或顏色值,來指定動畫播放期間的某些類型的動畫值。通過ValueAnimator類的一個工廠方法來獲取一個 ValueAnimator對象:ofInt()、ofFloat()、ofObject()。例如:

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

在這段代碼中,當start()方法開始執行時,ValueAnimator對象會在1000毫秒的動畫期間內,在0和1之間開始計算動畫的值。

還可以通過下面的做法來指定自定義的動畫類型:

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

在這段代碼中,在start()方法開始執行的時候,ValueAnimator對象會在1000毫秒的動畫期間內,使用由MyTypeEvaluator對象提供的邏輯,在startPropertyValue和endPropertyValue之間,開始計算動畫的值。

但是,在前一個代碼片段中,不會對一個對象形成實際的影響,因爲ValueAnimator對 象沒有直接在對象或屬性上執行操作。這麼做的最大可能是用這些計算值來修改那些想要動畫效果的對象。通過定義ValueAnimator類中響應的事件監 聽器,來處理動畫執行期間的重要事件,如幀更新等。當監聽器執行的時候,就能夠通過調用getAnimateValue()方法獲得指定幀的刷新的計算 值。有關監聽器的更多信息,請看“動畫監聽器”。

用ObjectAnimator來製作動畫

ObjectAnimator類是ValueAnimator類的一個子類,並且把時序引擎和ValueAnimator對象的計算值組合到一起,讓目標對象的命名屬性具備動畫能力。這樣使得讓任意對象具有動畫效果變的更加容易,如不在需要實現ValueAnimator.AnimatorUpdateListener接口,因爲被動畫的屬性會自動的更新。

實例化一個ObjectAnimator對象與實例化一個ValueAnimator對象類似,但是,它還需要跟動畫期間的參數一起,指定動畫對象和對象動畫屬性(用一個字符串)的名字:

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

要正確的更新ObjectAnimator對象的屬性,必須做以下事情:

1. 動畫效果的屬性必須有一個set<propertyName>格式的設置器方法。因爲在動畫處理期間,ObjectAnimator對象會自動的更新對應的動畫效果屬性,所以它必須使用這個設置器方法來訪問對應的屬性。例如,如果屬性名是foo,那麼就需要有一個setFoo()方法,如果這個設置器方法不存在,你有三種選擇:

   A. 如果你權利這麼做,就要在這個類中添加設置器方法;

   B. 使用一個你有權改變的包裝器類,並且這個包裝器能夠用一個有效的設置方法來接收動畫值,而且還要能夠把這個值轉發給初始對象。

   C. 使用ValueAnimator類來代替。

2.  如果你只在ObjectAnimator類的一個工廠方法中指定了一個values…參數,那麼該值會被假定爲動畫的結束值。因此,該對象的動畫效果屬性就必須要有一個獲取方法,用於獲得動畫的開始值。這個獲取方法必須使用get<propertyName>()格式。例如,屬性是foo,就必須有一個getFoo方法。

3.  動畫屬性的獲取(如果需要)和設置方法必須操作相同類型的指定給ObjectAnimator對象開始和結束值。例如,如果構建一個下面這樣的ObjectAnimator對象,就必須要有targetObejct.setPropName(float)和targetObject.getPropName(float)方法:


1
ObjectAnimator.ofFloat(targetObject, "propName", 1f)


4.  根據屬性或對象上的動畫效果,可能需要調用View對象上的invalidate()方法,在更新動畫效果時,強制屏幕重繪自己。在onAnimationUpdate()回調方法中做這件事情。例如,一個繪圖對象的顏色屬性的動畫效果,在隊形重繪自己時,纔會將變化結果更新到屏幕上。在View對象上的所有的屬性的設置器,如setAlpha()和setTranslationX()會正確的讓View對象失效,因此在調用這些方法設置新的值時候,你不需做失效這件事。有關監聽器的更多信息,請看“動畫監聽器”。

用AnimatorSet類來編排多個動畫

在很多場景中,一個動畫的播放要依賴與另一個動畫的開始或結束。Android系統讓你把這些相互依賴的動畫綁定到一個AnimatorSet對象中,以便能夠指定它們是同時的、順序的、或在指定的延時之後來播放。AnimatorSet對象也能夠彼此嵌套。

以下示例代碼來自Bouncing Balls示例,它按照以下方式播放Animator對象:

1.  播放bounceAnim

2.  同時播放squashAnim1、squashAnim2、stretchAnim1和stetchAnim2

3.  播放bounceBackAnim

4.  播放fadeAnim

1
2
3
4
5
6
7
8
9
10
11
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();

關於如何使用動畫集的完整示例,請看APIDemo中的Bouncing Balls示例。

動畫監聽器

使用下列介紹的監聽器能夠監聽動畫播放期間的重要事件:

1.  Animator.AnimatorListener

onAnimationStart()---動畫開始的時候被調用

onAnimationEnd()---動畫結束的時候被調用,它不管動畫是如何結束的。

onAnimationRepeate()---動畫重複播放的時候被調用

onAnimationCancel()---動畫被取消播放的時候被調用。

2.  ValueAnimator.AnimatorUpdateListener

onAnimationUpdate()---在動畫的幀上調用這個方法。通過監聽這個事件,使用在動畫期間由ValueAnimator對象產生的計算值。要使用這個值,就要用getAnimateValue()方法查詢傳遞到事件中的ValueAnimator對象,以便獲得當前的動畫值。如果使用ValueAnimator類,那麼實現這個監聽器是必須的。

根據屬性或對象的動畫效果,可能需要調用View對象上的invalidate()方法,用新的動畫值來強制屏幕的指定區域進行重繪。例如,Drawable對象的顏色屬性的動畫效果,在對象重繪自己的時候,只會導致屏幕的更新。在View對象上的所有屬性的設置器,如setAlpha()、setTranslationX()等方法都會正確的讓View對象失效,因此在調用這些方法設置新值的時候,你不需要讓該View對象失效。

如果不實現Animator.AnimatorListener接口的所有方法,你能夠繼承AnimatorListenerAdapter類,來代替對Animator.AnimatorListener接口的實現。AnimatorListenerAdapter類對這些方法提供了空的實現,你可以選擇性的重寫這些方法。

例如,APIDemo中的Bouncing Balls示例就只創建了一個AnimatorListenerdapter類的onAnimationEnd()回調方法:

1
2
3
4
5
6
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());
}

對於ViewGroups對象佈局變化的動畫

屬性動畫系統給ViewGroup對象的動畫變化提供了與View對象一樣容易動畫處理方法。

使用LayoutTransition類在ViewGroup內部處理佈局變化的動畫。當調用一個View對象的setVisibility()方法,或者設置該View的GONE常量,或者把該View對象添加到ViewGroup中(或者從ViewGroup中刪除)時,在ViewGroup內部的View對象就能夠實現時隱時現的動畫效果。當在ViewGroup對象中添加或刪除View對象時,其中的其他View對象也能夠動畫移動到新的位置。在LayoutTransition對象內通過調用setAnimator()方法,並且在傳遞給該方法的Animator對象參數中帶有下列LayoutTransition常量之一,就能夠定義該常量所代表的動畫:

1.  APPEARING---一個標記,它指示要在容器中正在顯示的項目上運行動畫;

2.  CHANGE APPEARING---一個標記,它指示在容器中由於新項目的出現而導致其他項目變化所要運行的動畫;

3.  DISAPPEARING---一個標記,它指示一個從容器中消失的項目所要運行的動畫;

4.  CHANGE_DISAPPEARING---一個標記,它指示由於一個項目要從容器中消失而導致其他項目的變化,所要運行的動畫。

能夠給這四種事件類型定義自定義動畫,以便定製自己的佈局過渡效果,也可以告訴動畫系統只使用默認的動畫效果。

在APIDemo中的LayoutAnimations示例,顯示瞭如何給佈局的過渡定義動畫效果,並且在想要動畫效果的View對象上設置動畫。

LayoutAnimationsByDefault類以及它對應的layout_animations_by_default.xml佈局資源文件顯示瞭如何在XML中啓用ViewGroup對象的默認佈局過渡效果。需要做的事情僅僅是把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,那麼在該ViewGroup對象中添加或刪除View對象,以及ViewGroup對象中其他的View對象都會自動的具有動畫效果。

使用TypeEvaluator

如果想要的動畫類型是Android系統所未知的,那麼通過實現TypeEvaluator接口就能夠創建自己的評價器。Android系統已知的類型是int、float或顏色(color),分別有IntEvaluator、FloatEvaluator和ArgbEvaluator類型的評價器所支持。

在TypeEvaluator接口中只有一個要實現的方法:evaluate()方法。這個方法允許正在使用的動畫處理器返回一個適用於於當前動畫時點動畫屬性值,FloatEvaluator類演示了這個方法是如何做這件事的:

1
2
3
4
5
6
7
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之間的值),然後根據你所使用的插值類型來計算一個要插入的動 畫的版本。插值比例是由TypeEvaluator對象通過fraction參數接收來的,因此在計算動畫值的時候,不需要考慮插值。

 

使用插補器

插補器定義了怎樣在動畫內指定用於時間函數的計算值。例如,指定貫穿整個動畫期間的線性播放動畫,意味在動畫整個時間裏都是均勻的移動,也能夠指定非線性動畫,如:在動畫的開始或結尾部分使用加速或減速的動畫。

在動畫系統中的插補器會接收一個來自Animator對象的一個比例,它代表了動畫已經過去的時間。插補器修改這個比例,使它與提供的目標動畫類型相吻合。Android系統在android.view.animation包中提供了一組共通的插補器。如果這個包中沒有適合你需要的,你可以實現TimeInterpolator接口來創建自己的插補器。

例如,以下是對AccelerateDecelerateInterpolator和LinearInterpolator插補器如何計算插補比例的比較。LinearInterpolator對延時比例沒有影響,AccelerateDecelerateInterpolator會讓動畫加速進入,並減速退出。以下是這些插補器方法中定義的邏輯:

AccelerateDecelerateInterpolator

1
2
3
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

1
2
3
public float getInterpolation(float input) {
    return input;
}

下表列出了一個持續1000毫秒的動畫通過插補器所計算的近似值:

播放時間(毫秒)

播放比例/插值比例(線性)

插值比例(加速/減速)

0

0

0

200

0.2

0.1

400

0.4

0.345

600

0.6

0.8

800

0.8

0.9

1000

1

1

如上表所示,LinearInterpolator插補器的計算結果是勻速變化的,每200毫秒增加0.2。AccelerateDecelerateInterpolator插補器的計算結果在200毫秒到600毫秒之間比LinearInterpolator的計算結果要快,而在600毫秒到1000毫秒之間則比LinearInterpolator的計算結果要慢。

關鍵幀

有時間和值構成的Keyframe對象會定義動畫在特定的時間點上特定的狀態。每個關鍵幀還有它自己的插補器來控制當前關鍵幀與前一個關鍵幀之間的動畫行爲。

要實例化一個Keyframe對象,必須使用以下工廠方法之一:ofInt()、ofFloat()、或ofObject()。使用這些工廠方法來獲取對應類型的關鍵幀,然後調用ofKeyframe工廠方法來獲取一個PropertyValuesHolder對象,一旦獲得了這個對象,就能夠得到一個在PropertyValuesHolder對象中傳遞的動畫製作器對象。以下代碼演示瞭如何做這件事情:

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

關於如何使用關鍵幀的完整的示例,情況APIDemo中的MultiPropertyAnimation示例。

http://developer.android.com/tools/samples/index.html

製作View動畫

屬性動畫系統允許對View對象的動畫進行簡化處理,並且在視圖動畫系統上提供了一些優點。視圖動畫系統通過改變View對象的繪製方式來轉換View對象。這種變換是在在每個View對象的容器中來處理的,因爲View對象本身沒有執行這種處理的屬性。這種處理會導致View對象產生動畫效果,但卻不會改變View對象自身。這樣即使在屏幕的不同的位置上繪製了View對象,該對象依然會保留在它的原始位置上。在Android3.0中,添加了新的屬性和對象的getter和setter方法,來消除這一缺陷。

屬性動畫系統能夠通過改變View對象中的實際屬性,讓View對象在屏幕上展現動畫效果。另外,View對象也會自動的調用invalidate()方法,在屬性發生變化時來屬性屏幕。在View類中便於動畫設置的新屬性有:

1. translationX和translationY:這兩個屬性作爲一種增量來控制着View對象從它佈局容器的左上角座標開始的位置。

2. rotation、rotationX和rotationY:這三個屬性控制View對象圍繞支點進行2D和3D旋轉。

3. scaleX和scaleY:這兩個屬性控制着View對象圍繞它的支點進行2D縮放。

4. pivotX和pivotY:這兩個屬性控制着View對象的支點位置,圍繞這個支點進行旋轉和縮放變換處理。默認情況下,該支點的位置就是View對象的中心點。

5. x和y:這是兩個簡單實用的屬性,它描述了View對象在它的容器中的最終位置,它是最初的左上角座標和translationX和translationY值的累計和。

6. alpha:它表示View對象的alpha透明度。默認值是1(不透明),0代表完全透明(不可見)。

要讓一個View對象的屬性具有動畫效果,如它的顏色或旋轉值等,就需要創建一個屬性動畫製作器,並給對象屬性指定想要的動畫效果,如:

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

用ViewPropertyAnimator製作動畫

ViewPropertyAnimator類使用一個單一的Animator對象,給一個View對象的幾個動畫屬性平行處理提供一種簡單的方法。它的行爲非常像ObjectAnimator類,因爲它修改了View對象屬性的實際的值,但是當多個動畫屬性同時處理時,它會更加高效。另外,使用ViewPropertyAnimator類的代碼更加簡潔和易於閱讀。以下代碼片段顯示了在同時處理View對象的x和y屬性動畫效果時,使用多個ObjectAnimator對象、一個單獨的ObjectAnimator對象和ViewPropertyAnimator對象之間的差異:

多個ObjectAnimator

1
2
3
4
5
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對象:

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

ViewPropertyAnimator對象:

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

在XML中聲明動畫

屬性動畫系統會讓你用XML來聲明屬性動畫,而不是用編程的方式來做它。通過XML中定義你的動畫,能夠更加容易的在多個Activity中重用動畫,並且更加容易的編輯動畫的播放順序。

從Android3.1開始,要把使用新的屬性動畫的API的動畫文件與那些使用傳統的視圖動畫框架區分開,你應該把屬性動畫的XML文件保存在res/animator/目錄中(而不是res/anim/)。animator目錄名是可選的,但是如果想要使用Eclipse ADT插件(ADT11.0.0+)中的佈局編輯器,就必須使用animator目錄,因爲ADT只搜索res/animator目錄中屬性動畫資源。

以下是屬性動畫類在XML聲明中所使用的對應的XML標籤:

以下示例順序的播放兩組對象動畫,第一組動畫中嵌套了一個同時播放兩個對象的動畫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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對象中的所有子對象設置給一個單一的目標對象。以下代碼顯示了做這件事的方法:

1
2
3
4
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章