close

Source: http://www.cnblogs.com/everhad/p/6277555.html

簡介

Android 3.0 (API level 11)引入了屬性動畫系統,它是一個完善的框架,可以用來對幾乎任何對象進行動畫。只需要指定要動畫的對象屬性,動畫時長,屬性值區間等,無論對像是否在屏幕中顯示與否,都可以隨時間改變其屬性值達到動畫效果。

屬性動畫支持以下特性:

  • Duration:動畫持續時間,默認為300ms;
  • Interpolation:插值器,隨動畫時間變化屬性值的公式。
  • Repeat Count & behavior:重復次數,重復類型(是否反轉)等。
  • Animator sets:動畫集合,若干動畫一起或依次執行。
  • Frame refresh delay:幀率,默認是10ms,但最終決定與系統的運行負載和對底層時鐘的響應速度。所給值僅僅是期望值。

工作原理

下面兩圖分別展示了對一個View對像的x坐標屬性執行不同屬性動畫時屬性隨時間變化的情況。動畫均持續40ms,使用系統默認的10ms更新一次位置x,動畫使得x從0增加到40。

  • 線性變化

可以看到x屬性值隨時間均勻變化。

  • 非線性變化

可以看到x屬性值隨時間不均勻變化。

動畫的本質就是“值隨時間變化”這樣的過程。
屬性動畫涉及的類型及其工作流程如下:

  • 首先使用ValueAnimator來定義動畫,它保存動畫的開始值startPropertyValue、結束值endPropertyValue,以及動畫持續時間duration;
  • 當執行方法start()后,動畫開始。
  • 動畫開啟后,隨著時間行進,每當到達一個幀頻率(或者說刷新頻率)后——比如上面的默認的是每過10ms后,會觸發一次動畫幀生成。

  • 接下來,每次生成動畫幀,進入計算動畫值的過程。

  • 計算動畫時間進度(elapsed fraction):屬性動畫系統抽象了對動畫時間的表示,我們不要去假設它和真實時間的關系。實際上時間都表示為相對某一時刻所經過的毫秒數,任何使用時間作為參數的系統只需要保證時間參考點的唯一有效即可。
    屬性動畫使用最終的時間進度——而非具體的時間增量來表示動畫的時間進展,這個時間一般稱作“邏輯時間”,和View Animation是一樣的。最終動畫時間進度是0到1間的一個float數值,它是經過的時間t和動畫總時間duration的比值,表示動畫進度從0%到100%。比如上面案例中,t=10ms時動畫時間進度為0.25f=25%=10ms/40ms;

  • 計算屬性值變化百分比:時間進度確定后,為了讓動畫值變化和時間進度保持某種映射關系以表現出特殊動畫效果,如加速減速運動等,需要用到“插值器”——TimeInterpolator,它根據動畫時間進度得到一個最終的屬性值變化百分比——叫做插值分數(interpolated fraction),插值器其實就是一個函數,根據時間進度得出變化百分比。

時間進度、TimeInterpolator根據時間進度得到的結果“插值分數”都是一個百分數,后者表示從startPropertyValue到endPropertyValue的差值的百分比。需要注意的是時間進度的值只能是0~1f,而interpolated fraction可以是<0或>1的,比如展示像左右搖擺最后固定在某處這樣的效果。

  • 計算屬性值:得到t時刻的屬性值變化百分比后,需要獲得對應的實際屬性值。因為不同對像的不同Property的數據類型是不一樣的,屬性動畫中使用TypeEvaluator來抽象對目標屬性的計算,它定義了方法evaluate(float fraction, T startValue, T endValue)根據插值分數及屬性的startValue和endValue來得到fraction對應的實際屬性值。

上面示例中的x是int類型的,它對應IntEvaluator。 針對上面的“線性變化”的動畫,t=20ms時,時間進度為0.5f,線性插值器映射得到的變化百分比依然是0.5f,最終得到得屬性值為(40 - 0) x 0.5f = 20。

  • 改變對像屬性值:得到t時刻的動畫值后,框架會修改目標view對像的屬性x的值。

以上就是屬性動畫涉及的關鍵類型以及它們之間的工作關系。

屬性動畫和View動畫的區別

View動畫的限制包括:

  • 只能針對View對像,而且不是所有屬性,如background color無法默認被動畫,需要自己編寫邏輯實現不支持的non-view對像及屬性。

  • 動畫僅改變了view的繪制內容,而它的位置信息沒有變化。

屬性動畫沒有以上限制,可以針對任何對像的任何屬性,而且真實地改變了這些屬性。
屬性動畫系統地設計也更加健壯,可以在很高的抽象層次上自定義插值器,或者同步多個Animator等。

從框架實現上看,對view animation的支持直接貫穿了View、ViewGroup這些類型的源碼,代碼入侵性較大。
而屬性動畫可以是針對任意類型對象的,它是以組合的方式,實現上僅僅是調用目標對象的方法,幾乎不對要動畫的類型做要求。所以設計上看屬性動畫更合理些。

View動畫使用上更加簡單些,可以根據需求選擇其中之一。

屬性動畫框架總攬

相關類型都在包android.animation下,View動畫android.view.animation里面的一些interpolators也可以在屬性動畫中使用。

Animator

Animator表示屬性動畫的抽象。它的子類有:
AnimatorSet, ValueAnimator 、 ObjectAnimator, TimeAnimator 等。

Evaluator

Animator實例使用Evaluator來對對像屬性執行計算,其接口為:

public interface TypeEvaluator<T> {
   public T evaluate(float fraction, T startValue, T endValue);
}

在上面“工作原理”中的描述已經很容易明白evaluate()方法的功能,其邏輯為
returnValue = startValue + fraction * (endValue - startValue);

TypeEvaluator的實現類有:
ArgbEvaluator, FloatArrayEvaluator, FloatEvaluator, IntArrayEvaluator, IntEvaluator, PointFEvaluator, RectEvaluator

Interpolators

A time interpolator defines how specific values in an animation are calculated as a function of time.
根據動畫時間進度得到屬性值的變化百分比。

接口TimeInterpolator是插值器接口:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {    
  /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

參數input是時間進度百分比,getInterpolation()返回映射后的屬性值變化百分比。
它的實現類有:
LinearInterpolator、DecelerateInterpolator、BounceInterpolator等。

使用ValueAnimator

ValueAnimator用來在指定的startValue和endValue區間內執行動畫,產生一系列動畫值。
它提供了若干靜態工廠方法來獲得針對不同類型數據的實例:
ofInt(), ofFloat(), or ofObject().

使用方式:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.setRepeatCount(20);
// 反轉REVERSE/重復RESTART/無限INFINITE
animation.setRepeatMode(ValueAnimator.INFINITE);
animation.start();
animation.cancel();

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

使用方法ofObject()對自定義類型等非基礎類型進行動畫。
ValueAnimator可以設置重復次數、重復模式,可以取消等。

start()方法執行后動畫開始,之后會回調動畫監聽器的各個方法,ValueAnimator不和某個具體的對像關聯(其setTarget()是空實現),它執行的動畫是針對startValue、endValue這樣的抽象數據進行的,所以不會自動產生對任何對像屬性的影響。

使用ValueAnimator時需要結合“Animation Listeners”來獲得動畫執行的回掉,然后自己調用getAnimatedValue()來得到當時的動畫值后自己去更新目標對像的屬性值。

“Animation Listeners”的使用稍后了解。

使用ObjectAnimator

ObjectAnimator 是ValueAnimator的子類,所以它擁有“動畫時間引擎”和屬性值計算的能力,同時又支持關聯目標對像,這樣對目標對像就可以在動畫過程中自動更新其屬性。而ValueAnimator則必須結合“Animation Listeners”手動更新。

ObjectAnimator的創建類似ValueAnimator,但需要目標對像和屬性名參數:

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

使用ObjectAnimator需要注意:

  • 動畫更新的對像需要具備屬性名對應的setter方法:set

  • 上面強調的是ValueAnimator的startValue和endValue,實際上可以指定1~n個值,它們作為動畫過程中的若干中間值。如果僅指定一個值,那么它被作為endValue。此時startValue自動根據對像當前目標屬性生成,這時就需要對應屬性的getter了。

  • getter和setter必須是同樣的數據類型。因為對應的startValue和endValue是一樣的。

  • 雖然ObjectAnimator自動更新目標對像屬性,但如果需要正常顯示的話,一些屬性不會自動重繪view對像,此時就需要手動調用對像的invalidate()來完成——通過動畫監聽器的onAnimationUpdate() 回調,后面會了解。如對一個view對像背景Drawable 對像修改顏色;而那些直接操作View的方法 setAlpha() 、setTranslationX() 等本身會自動重繪view。

使用AnimatorSet

可以使用AnimatorSet組合多個ValueAnimator,可以是其它的AnimatorSet。
最終使得一些動畫在另一些動畫開始前或結束后執行,多個動畫同時或依次執行,或其中一些動畫延遲執行等。

下面是API demo中一個案例的代碼片段,其功能是:

  1. Plays bounceAnim.
  2. Plays squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2 at the same time.
  3. Plays bounceBackAnim.
  4. Plays 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();

Animation Listeners

通過動畫監聽器可以對動畫過程的重要事件進行監聽,獲得回調。

Animator.AnimatorListener

監聽動畫生命周期特殊事件。

  • onAnimationStart() - Called when the animation starts.
  • onAnimationEnd() - Called when the animation ends.
  • onAnimationRepeat() - Called when the animation repeats itself.
  • onAnimationCancel() - Called when the animation is canceled. A cancelled * animation also calls onAnimationEnd(), regardless of how they were ended.

ValueAnimator.AnimatorUpdateListener

監聽動畫幀更新,它就一個onAnimationUpdate() 方法,在產生每一幀時被回調。

在方法onAnimationUpdate()中可以調用getAnimatedValue()獲得當前的動畫值,ValueAnimator的實例必須添加AnimatorUpdateListener來獲得動畫值序列。而ObjectAnimator在更新一些對像的某些屬性——如view的background顏色時,也需要使用此接口手動執行一些邏輯。

AnimatorListenerAdapter是AnimatorListener的一個適配類,如果僅需要獲得部分回調時它作為一種簡化。

LayoutTransition

針對ViewGroup,在其childViews被add、remove、可見性發生變化、位置變化等事件發生時,如果希望執行一些布局相關的動畫,可以使用LayoutTransition。

LayoutTransition定義了表示布局變化的若干常量,可以使用這些常量來指定關聯的ValueAnimator到LayoutTransition,最后使用LayoutTransition來使得一個ViewGroup擁有布局變動時的動畫效果。

常量包括:

  • APPEARING - A flag indicating the animation that runs on items that are appearing in the container.
  • CHANGE_APPEARING - A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.
  • DISAPPEARING - A flag indicating the animation that runs on items that are disappearing from the container.
  • CHANGE_DISAPPEARING - A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.

首先要在布局文件中為ViewGroup設置屬性android:animateLayoutChanges為true:

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

在代碼中:

// container是目標ViewGroup
ViewGroup container = (ViewGroup)findViewById(R.id.container);
final LayoutTransition transitioner = new LayoutTransition();
container.setLayoutTransition(transitioner);
transition.setAnimator(LayoutTransition.APPEARING, customAppearingAnim);

方法setAnimator(int transitionType, Animator animator)用來設置某種布局變動時執行的Animator動畫。

之后當container中childViews發生變化時,對應動畫就自動執行。

使用TypeEvaluator

如果目標對象屬性的數據類型就不是IntEvaluator, FloatEvaluator這些支持的,就需要自己定義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);
    }
}

參數fraction是根據動畫時間進度以及interpolator得到的變化百分比,也就是已經是插值計算后的結果值。

使用Interpolator

根據Animator傳遞的動畫時間進度返回動畫值的變化百分比。
Animator的duration和repeats決定了動畫的時間和周期行為,而Interpolator決定了動畫過程
的表現。
比如LinearInterpolator:

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

是一個和動畫時間無關的常量。自己可以根據需要提供Interpolator實現類來完成需要的動畫過程。

指定Keyframes

一個Keyframe表示動畫過程某個時間點上的動畫值,默認的startValue、endValue就是開始結束
時的兩個Keyframe。
可以為Animator指定若干Keyframe來精確控制其動畫過程,而且每個Keyframe可以設置通過setInterpolator()來指定自己的插值器——上一個Keyframe執行到此Keyframe時使用。

ValueAnimator.ofFloat()、setFloatValues()等實際上都是在指定動畫的關鍵幀。
示例代碼:

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf0 = Keyframe.ofFloat(0.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);

對Views執行動畫

view animation的動畫是通過其父容器來繪制動畫效果,而自身屬性無法變化,這樣帶來一些UI上的奇怪問題。Property動畫則實際改變了View對象的屬性,其底層原理正是框架調用view對象的setter、getter實現。如果沒有那么也可以自己再onAnimationUpdate()中主動設置對象屬性,并調用invalidate()通知重繪。
可以想象屬性動畫系統應該是通過反射來調用這些屬性對應的setter/getter方法的。
為了配合新的屬性動畫,View類添加了一些對應其UI屬性的getter/setter:

  • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates which are set by its layout container.
  • rotation, rotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
  • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
  • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
  • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
  • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).

可以像這樣來獲得一個對View進行旋轉的動畫:

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

ViewPropertyAnimator

如果一次同時修改一個對象的若干屬性,那么ViewPropertyAnimator提供了一種便捷的方式。
它內部使用一個Animator對象來實現功能,和ObjectAnimator表現上很接近,使用更加簡單。

下面的代碼實現相同的功能,展示了這點:

  • 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();
  • 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文件定義,復用性更好,且編輯簡單。
為了區分之前的view animation,建議在res/animator/下放置屬性動畫的資源文件。

下面是一個示例:

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

標簽對應的類型:

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

可見xml聲明方式定義屬性動畫更加方便,閱讀性好。尤其是多個動畫的同時、依次執行的設置。

在代碼中:

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

方法setTarget()用來關聯要動畫的目標View,而屬性名稱在xml中就可以指定。

補充

  • 動畫時間進度
    如果動畫是循環的,那么要知道,時間進度始終是一次動畫過程中的百分比,即duration。

資料

  • api docs:/docs/guide/topics/graphics/prop-animation.html

(本文使用Atom編寫)


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()