Android應用開發——Property Animation

聲明:下面的內容需要Android API level 11的支持

Property Animation是如何運作的

首先,來看一下兩個不一樣的Property Animation場景:
  • 場景一(Linear Animation):Animation要求一個物體A的x屬性在40ms內勻速地從0px變化到40px。幀的刷新率默認爲10ms/幀。
  • 場景二(Not-Linear Animation):Animation要求一個物體A的x屬性在40ms內先加速後減速地從0px變化到40px。幀的刷新率默認爲10ms/幀。
接着,看一下Property Animation系統中ValueAnimator的一些重要的組件,及其運作的流程圖:
下面是關於上面三張圖片的概括:
  • ValueAnimator對象持續跟蹤animation的時間點,如animation運行了多長時間,屬性(如場景一二中的x屬性)的當前值。
  • ValueAnimator封裝了一個TimeInterpolator,TimeInterpolator定義了animation是如何插值的。ValueAnimator也封裝了一個TypeEvaluator,TypeEvaluator定義瞭如何計算屬性的值。例如示例2中,TimeInterpolator使用的是AccelerateDecelerateInterpolator和TypeEvaluator使用的是IntEvaluator
  • 創建了一個ValueAnimator併爲ValueAnimator注入animation的starting value、ending value、duration value後,調用start()方法來啓動animation。
  • 在Animation的整個運行過程中,ValueAnimator會基於動畫運行的時間計算出一個0-1的值,這個值被稱爲elapsed fraction。如場景一t=10ms的時刻,elapsed fraction的值爲0.25,因爲整個動畫的運行時間是40ms,所以elapsed fraction=10/40。
  • 在ValueAnimator完成elapsed fraction的計算之後,它會調用TimeInterpolator去計算interpolated fraction的值。interpolated fraction會根據elapsed fraction的值來計算。例如,在場景二中,由於是緩慢加速的原因,在t=10ms的時刻,interpolated fraction的值爲0.15,會小於elapsed fraction的值0.25。而在場景一中,由於是勻速的關係,interpolated fraction的值會永遠與elapsed fraction的值一樣的。
  • 在ValueAnimator完成elapsed fraction的計算之後,它會調用合適的TypeEvaluator去計算屬性的當前時刻的值,這個計算過程是基於interpolated fraction的值、starting value和ending value。例如,在場景二t=10ms的時刻點,interpolated fraction的值爲0.15,因此屬性x的值便會是0.15X(40-0)=6.

Property Animation API概覽

android.animation包中囊括了大部分property animation系統的API,另外,在android.view.animation包中定義許多插值器(interpolator),同樣可以在property animation中使用這些插值器。

下面是一些主要組件的介紹:
  • Animator:它提供了創建animation最基本的結構,基本上不會直接地用到這個類,要使用這個類,必須繼承它並實現需要的功能。
  • ValueAnimator:property animation主要的時間引擎,它計算了各個幀的屬性值。它包括所有核心功能:計算每個幀的相關內容,負責接收更新事件,按屬性值的類型自定義計算規則。一個動畫需要完成兩大塊內容:計算各幀的相關內容和併爲對象設置這些計算後的值。ValueAnimator不負責第二塊內容,因此你必須由ValueAnimator監聽計算值的更新並修改對象相關屬性值。
  • ObjectAnimator:ValueAnimator子類,它允許你設置一個對象的屬性來完成動畫。ObjectAnimator更加常用,因爲使用它來建立動畫會更加簡單。然而,有時你需要用到ValueAnimator,因爲ObjectAnimator會有一些限制。
  • AnimatorSet:把多個Animator捆綁在一起,可以讓他們同時播放,或按順序地播放、或延時一段時間播放。
Evaluators用於告訴Property Animation系統如何去計算屬性值。Property Animation系統提供了下面的一些Evaluator:
  • IntEvaluator:用於計算Int類型屬性值的計算器。
  • FloatEvaluator:用於計算Float類型屬性值的計算器。
  • ArgbEvaluator:用於計算以16進制形式表示的顏色值的計算器。
  • TypeEvaluator:一個計算器接口,它允許你創建你自己的計算器。如果你正在計算一個對象屬性並不是int,float或者顏色值類型的,那麼你必須實現TypeEvaluator接口去指定如何去計算對象的屬性值。
在android.view.animation包中定義的TimeInterpolator:


使用ValueAnimator創建動畫

要獲取ValueAnimator實例,可以通過調用它的工廠方法: ofInt()ofFloat(), or ofObject()例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
上面的例子實現了在1000ms內,值從0-1的變化。也可以提供一個自定義的Evaluator:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

上面的代碼片段中,ValueAnimator僅僅是計算動畫過程中變化的值,並沒有把這些計算出來的值應用到具體的對象上面,所以也不會有什麼的動畫顯示出來。要把計算出來的值應用到對象上,必須爲ValueAnimator註冊一個監聽器ValueAnimator.AnimatorUpdateListener,該監聽器負責更新對象的屬性值。在實現這個監聽器的時候,可以通過getAnimatedValue()的方法來獲取當前幀的值。


使用ObjectAnimator創建動畫

ObjectAnimator與ValueAnimator的區別是ObjectAnimator可以直接地把動畫過程中計算出來的的值應用到一個具體對象的屬性上,而ValueAnimator需要另外註冊一個監聽器來完成這個工作。所以使用ObjectAnimator就不再需要實現ValueAnimator.AnimatorUpdateListener了。

實例化ObjectAnimator相似於ValueAnimator,但你還需要指定具體的對象和對象的屬性名(字符串形式)
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
爲了讓ObjectAnimator能正常地運作,你還需要注意下面幾點:
  • 要爲對應的對象提供setter方法,如上例中需要爲foo對象添加setAlpha(float value)方法。在不能修改對象源碼的情況下,要不先對對象進行封裝(extends),或者使用ValueAnimator。
  • 如果ObjectAnimator的工廠方法中的values... 參數提供了一個值(原本需要提供起始值和結束值),那麼該值會被認爲是結束值。起始值需要通過對象的getter方法提供,因此,在這種情況下,還需要提供對應屬性的getter方法。
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  • 如果動畫的對象是View,那麼就可能需要在 onAnimationUpdate() 回調函數中調用View.invalidate()方法來刷新屏幕的顯示。比如說,設置Drawable對象的color屬性。但是,View中的所有setter方法,如 setAlpha() and setTranslationX()會自動地調用invalidate()方法,因此不需要額外地調用invalidate()方法。

使用AnimatorSet編排多個Animator

有些時候,某些動畫的開始需要依賴於其他動畫的開始或結束,這時候就可以使用AnimatorSet來綁定這些Animator了。如:
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();
上面的代碼片段,動畫的執行順序如下:
  1. 播放bounceAnim動畫
  2. 同時播放squashAnim1squashAnim2stretchAnim1, and stretchAnim2
  3. 播放bounceBackAnim
  4. 播放fadeAnim

Animation監聽器

你可以使用下面的監聽器監聽一些重要的事件:
另外,還可以繼承適配器 AnimatorListenerAdapter來代替對Animator.AnimatorListener的接口的實現,那麼就只需要實現你所關心的方法了。如:
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佈局改變時的應用

在ViewGroup佈局發生改變的時候(如ViewGroup中的View消失或顯示的時候),這個變化的過程你可能想要用動畫的形式表現出來。ViewGroup通過方法
setLayoutTransition(LayoutTransition)來設置一個佈局轉換的動畫。 在LayoutTransition 上可以通過調用 setAnimator()方法來設置Animator,另外,還需要向這個方法傳遞一個 LayoutTransition標誌常量,這個常量指示着在什麼時候執行這個animator,可用的常量有:
  • APPEARING - 指示layout中的view正要顯示的時候運行動畫
  • CHANGE_APPEARING - 指示layout中因爲有新的view加入而改變layout時運行動畫
  • DISAPPEARING - 指示layout中的view正要消失的時候運行動畫
  • CHANGE_DISAPPEARING - 指示layout中有view消失而改變layout時運行動畫
如果你想要使用系統默認的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" />


自定義Evaluator

IntEvaluatorFloatEvaluator, and ArgbEvaluator 這三種計算器都不能滿足你的要求的時候,你可以通過實現 TypeEvaluator 接口的 evaluate()方法來自定義一個Evaluator。
下面是FloatEvaluator的evaluate()的實現:
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);
    }
}


自定義Interpolator

自定義Interpolator是通過實現TimeInterpolator 的getIntepolation(float)方法來實現的。下面展示的分別是AccelerateDecelerateInterpolator和LinearInterpolator的具體實現,其中input爲Elapsed fraction(時間因子)
  • 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;
    }

使用Keyframes方式創建動畫

 Keyframe 對象由elapsed fraction/value對組成。 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);

使用ViewPropertyAnimator創建動畫

使用ViewPropertyAnimator可以很方便地根據View的多個屬性值創建動畫。下面用多個ObjectAnimator方式、單個ObjectAnimator方式和ViewPropertyAnimator方式創建View多屬性變化的動畫作對比。
  • 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方式聲明Animator

可以在res/animator目錄下聲明Property Animation的動畫(注意不是res/anim,它是View Animation框架的資源目錄)

Animator類與標籤元素的對應關係:
下面是res/animator/property_animator.xml的配置示例:
<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>

爲了能夠運行上面聲明的animator,必須在代碼中使用AnimatorInflater的inflate方法來實例化xml聲明的animator,如:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();


發佈了30 篇原創文章 · 獲贊 31 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章