Android動畫之屬性動畫

屬性動畫可以在以下幾個方面對動畫進行設置:

  • Duration:動畫持續時間,默認是300ms。
  • Time interpolation:插值器,可以指定屬性值如何根據動畫當前播放時間進行變換。
  • Repeat count and behavior:重複次數和重複方式,正向或者逆向重複。
  • Animator sets:動畫集合,可以對動畫進行分類組合,指定播放順序或者播放延時。
  • Frame refresh delay:可以指定多久更新一次動畫的幀,默認是10ms更新一次,但是應用更新動畫的速度很大程度上取決於系統的繁忙程度,以及系統可以多快響應內含的定時器。

工作原理

這裏寫圖片描述
ValueAnimator這個類記錄着動畫的計時,還封裝了TimeInterpolator和TypeEvaluator兩個類。TimeInterpolator用來計算時間到動畫執行比例的變換,TypeEvaluator用來計算變化的屬性值。

當ValueAnimator調用start()之後,ValueAnimator會根據動畫時長和已流逝時間來計算時間流逝百分比。計算完時間流逝百分比後,ValueAnimator就會調用當前的TimeInterpolator來計算插值調整過後的實際百分比,然後根據實際百分比調用TypeEvaluator來計算當前的屬性值。

ValueAnimator的使用

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
// 自定義類型求值器
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
// 監聽值的變化
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

ObjectAnimator的使用

ObjectAnimator是ValueAnimator的子類,幫我們把ValueAnimator改變後的值設置到對象上面,省去設置監聽的步驟。

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

如果要讓ObjectAnimator可以正常更新屬性值,必須要滿足以下幾個條件:

  • 所要設置動畫的對象屬性必須有對應的setter方法,如果沒有的話,可以採取三種方案之一:1. 有權限修改這個類的話直接加一個setter方法 2. 使用一個包裝類,包裝類包含setter,然後再把設置的值轉給目標對象屬性。3. 使用ValueAnimator。
  • 如果ObjectAnimator的工廠方法中values…可變參數的個數是一個,那這個參數值默認是動畫的結束值,因此該對象屬性必須有一個對應的getter方法來獲取動畫的初始值。
  • 如果有getter,和setter操作的屬性類型必須是一樣的。
  • 根據執行動畫的特定屬性或者對象,可能需要調用invalidate()來強制重繪,可以在onAnimationUpdate()回調方法中操作。

使用動畫集合播放多個動畫

可以使用AnimatorSet來控制多個動畫的播放,AnimatorSet可以自己嵌套自己。

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對象的getAnimatedValue()來獲取當前值。

如果不想要實現AnimatorListener中的所有回調,可以繼承AnimatorListenerAdapter來選擇自己需要的回調去重寫。

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的佈局動畫

屬性動畫在給View對象提供動畫的同時,也可以用來給ViewGroup的佈局變化設置動畫。

佈局動畫是通過LayoutTransition類來實現。佈局動畫指的是ViewGroup當中View的添加或刪除,或者可見性的改變,對相應的View進行動畫出現或者消失。其他View也可以動畫移動到新的位置上。

通過調用LayoutTransition對象的setAnimator()方法來設置動畫,可以選擇以下的常量來設置:

  • APPEARING:表示這個動畫用來作用在正在顯示的View。
  • CHANGE_APPEARING :表示這個動畫用來作用在因爲其他新顯示View而改變位置的View上。
  • DISAPPEARING :表示這個動畫用來作用在正在消失的View。
  • CHANGE_DISAPPEARING :表示這個動畫用來作用在因爲其他新消失View而改變位置的View上。

如果要使用系統默認的佈局動畫,只要加上animateLaoutChanges=true即可:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

使用TypeEvaluator

系統已經實現的TypeEvaluator包括IntEvaluator、FloatEvaluator、ArgbEvaluator,如果要對自定義類型進行動畫,那就要實現TypeEvaluator接口:

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

使用插值器

插值器是用來計算屬性值如何隨着動畫流逝比例進行變化的。系統提供的插值器在android.view.animation包中,如果要自定義插值器可以實現TimeInterpolator接口。

兩種系統插值器的實現:
LinearInterpolator:

public float getInterpolation(float input) {
    return input;
}

AccelerateDecelerateInterpolator:

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

指定關鍵幀

一個Keyframe對象用來指定某個時間點的具體屬性值,兩個Keyframe之間還可以指定interpolator來控制中間的變化行爲。

具體使用就是先調用工廠方法獲取Keyframe對象,然後調用PropertyValuesHolder的靜態方法ofKeyframe()獲取PropertyValuesHolder對象,再調用ObjectAnimator的靜態ofPropertyValuesHolder()獲取ObjectAnimator。

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也可以進行多屬性動畫,不過會比較麻煩。

1. 單純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();

2. 配合ViewPropertyAnimator進行多屬性動畫

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

另一種更簡潔的用法:

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

ObjectAnimator

ObjectAnimator有個特殊的rotateX和rotateY屬性,是在三維維度上進行動畫的。

在XML中聲明動畫

爲了跟補間動畫區分開來,屬性動畫的XML文件目錄是res/animator/。

以下爲類及其對應的XML標籤:

  • ValueAnimator : <animator>
  • ObjectAnimator : <objectAnimator>
  • AnimatorSet : <set>

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>

XML動畫的使用:

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

ValueAnimator示例:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

動畫對UI性能的影響

更新UI的動畫會對UI每一幀的渲染增加額外的工作,是在渲染管道的動畫階段進行的,可以通過啓用Profile GPU Rendering來查看動畫階段的性能表現。

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