1. 程式人生 > >Android動畫深入分析——屬性動畫

Android動畫深入分析——屬性動畫

屬性動畫是在API11中引入的特性,和View動畫不同,它對作用物件進行了擴充套件,屬性動畫可以對任何物件做動畫,甚至還可以沒有物件。除了作用物件進行擴充套件以外,屬性動畫的效果也得到了加強,不再像View動畫那樣只能支援四種簡單的變換。屬性動畫中有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通過它們可以實現絢麗的動畫。

1.使用屬性動畫

屬性動畫可以對任意物件進行動畫而不僅僅是View,動畫預設時間間隔300ms,預設幀率10ms/幀。其可以達到的效果是,在一個時間間隔內完成物件從一個屬性值到另一個屬性值的改變。因此屬性動畫幾乎是無所不能的,只要物件有這個屬性,它都能實現動畫效果。但是屬性動畫從API11才有,這就嚴重製約了屬性動畫的使用。可以採用開源動畫庫nineoldandroids來相容以前的版本,採用nineoldandroids
,可以在API11以前的系統上使用屬性動畫,nineoldandroids的網址是:http://nineoldandroids.com。nineoldandroids對屬性動畫做了相容,在API 11以前的版厄本那其內部是通過代理View動畫來實現的,因此在低Android版本上,它的本質是View動畫,儘管使用方法看起來是屬性動畫。nineoldandroids的功能和系統原始對android.animation.*中類的功能完全一致,使用方法也完全一樣,只要我們用nineoldandroids來編寫動畫,就可以在所有的Android系統上執行。比較常用的幾個動畫類是:ValueAnimator、ObjectAnimator和AnimatorSet
,其中ObjectAnimator繼承自ValueAnimatorAnimatorSet是動畫集合,可以定義一組動畫,它們使用起來也是及其簡單的。如何使用屬性動畫呢?下面舉幾個小李子,讀者以看就明白了。
(1)改變一個物件(myObject)的translationY屬性,讓其沿著Y軸向上平移一段距離:它的高度,改動畫在預設時間內完成,動畫完成時間是可以自定義的。想要靈活的效果我們還可以定義插值器和估值演算法,但是一般來說我們不需要自定義,系統已經預置了一些,能夠滿足常用的動畫。
        ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
(2)改變一個物件的背景色屬性,典型的情形是改變View的背景色,下面的動畫可以讓背景色在3秒內實現從0xFFFF8080到0xFF8080FF的漸變,動畫會無線迴圈而且會有反轉的效果。
        ValueAnimator colorAnim = ObjectAnimator.ofInt(button, "backgroundColor",0xFFFF8080,0xFF8080FF);
        colorAnim.setDuration(3000);
        colorAnim.setEvaluator(new ArgbEvaluator());
        colorAnim.setRepeatCount(ValueAnimator.INFINITE);
        colorAnim.setRepeatMode(ValueAnimator.REVERSE);
        colorAnim.start();
(3)動畫集合,5秒內對View的旋轉、平移、縮放和透明度都進行了改變。
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(button,"rotationX", 0,360),
                ObjectAnimator.ofFloat(button,"rotationY", 0,180),
                ObjectAnimator.ofFloat(button,"rotation", 0,-90),
                ObjectAnimator.ofFloat(button,"translationX", 0,90),
                ObjectAnimator.ofFloat(button,"translationY", 0,90),
                ObjectAnimator.ofFloat(button,"scaleX", 1,1.5f),
                ObjectAnimator.ofFloat(button,"scaleY", 1,0.5f),
                ObjectAnimator.ofFloat(button,"alpha", 1,0.25f,1)

        );
        set.setDuration(5000).start();
        set.start();
屬性動畫除了通過程式碼實現以外,還可以通過XML來定義。屬性動畫需要定義在res/animator/目錄下,它的語法如下所示。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together|sequentially">
    <objectAnimator android:propertyName="string"
        android:duration="int"
        android:valueFrom="float|int|color"
        android:valueTo="float|int|color"
        android:valueType="pathType|floatType|intType|colorType"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode="restart|reverse"/>
    <animator android:duration="int"
        android:valueFrom="float|int|color"
        android:valueTo="float|int|color"
        android:valueType="pathType|floatType|intType|colorType"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode="restart|reverse"/>
    <set>
        ...
    </set>
</set>
屬性動畫的各種引數都比較好理解,在XML中可以定義ValueAnimator、ObjectAnimator以及AnimatorSet,其中<set>標籤對於AnimatorSet,<objectAnimator>標籤則對應ObjectAnimator,<animator>對應ValueAnimator。<set>標籤的android:ordering屬性有兩個可選值:together和sequentially,其中together表示動畫集合中的子動畫同時播放,sequentially則表示動畫集合中的子動畫按照前後順序依次播放,android:ordering屬性預設值是together。對於<objectAnimator>標籤的各個屬性的含義,下面簡單說明一下,對於<animator>標籤這裡就不再介紹了,因為它只是比<objectAnimator>少了個android:propertyName屬性而已,其他都是一樣的。android:propertyName——表述屬性動畫的作用物件的屬性的名稱;android:duration——表示動畫的時長;
android:valueFrom——表示屬性的起始值;android:valueTo——表示屬性的結束值;android:startOffset——表示動畫的延遲時間,當動畫開始後,需要延遲多說毫秒才會真正播放此動畫;android:repeatCount——表示動畫的重複次數;android:repeatMode——表示動畫的重複模式;android:valueType——表示android:propertyName所指定的屬性的型別,有intType和floatType兩個可選項,分別表示屬性的型別為整型和浮點型。另外,如果android:propertyName所指定的屬性表示的是顏色,那麼不需要指定android:valueType,系統會自動對顏色型別的屬性做處理。對於一個動畫來說,有兩個屬性這裡要特殊說明一下,一個是android:repeatCount,它表示動畫迴圈的次數,預設值為0,其中-1表示無線迴圈;另外一個是android:repeatMode,它表示動畫迴圈的模式,有兩個選項:repeat和reverse,分別表示連續重複和逆向重複。連續重複比較好理解,就是動畫每次都重寫開始播放,而逆向重複是指第一次播放完以後,第二次會倒著播放,第三次再重投開始播放動畫,第四次再到這播放動畫,如此反覆。下面是一個具體的例子,我們通過XML定義一個屬性動畫並將其作用再View上,如下所示。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator android:propertyName="translationX"
        android:duration="3000"
        android:valueTo="100"
        android:valueType="floatType"/>

    <objectAnimator android:propertyName="translationY"
        android:duration="3000"
        android:valueTo="300"
        android:valueType="floatType"/>
</set>
如何使用上面的屬性動畫呢?也很簡單,如下所示。
        AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.property_animator);
        set.setTarget(button);
        set.start();
在實際的開發中建議採用程式碼來實現屬性動畫,這時因為通過程式碼來實現比較簡單。更重要的是,很多時候一個屬性的起始值是無法提前確定的,比如讓一個Button從螢幕左邊移動到螢幕的右邊,由於我們無法提前知道螢幕的寬度,因此無法將屬性動畫定義在XML中,在這種情況下就必須通過程式碼來動態的建立屬性動畫。

2.理解插值器和估值器

TimeInterpolator中文翻譯為時間插值器,它的作用是根據時間流逝的百分比來計算出當前屬性值改變的百分比,系統預知的有LinearInterpolator(線性插值器:勻速動畫)、AccelerateDecelerateInterpolator(加速減速插值器:動畫兩頭慢中間快)和DecelerateInterpolator(減速插值器:動畫越來越慢)等。TypeEvaluator的中文翻譯為型別估值演算法,也叫估值器,它的作用是根據當前屬性改變的百分比來計算改變後的屬性值,系統預知的有IntEvaluator(針對整型屬性)、FloatEvaluator(針對浮點型屬性)和ArgbEvaluator(針對Color屬性)。屬性動畫中的插值器(Interpolator)和估值器(Evaluator)很重要,它們是實現非勻速動畫的重要手段。可能這麼說還有點晦澀,沒關係,下面各處一個示例就很好理解了。如下圖所示,他是一個勻速動畫,採用了線性插值器和整型估值演算法,在40ms內,View的x屬性從0到40的變換。由於動畫的預設屬性率為10ms/幀,所有改動畫將分為5幀進行,我們來考慮第三幀(x=20, t=20ms),當t=20ms的時候,時間的流逝的百分比是0.5(20/40=0.5),意味著現在時間過了一般,那x應該改變多說呢?這個就是由插值器和估值演算法來確定。拿線性插值器來說,當時間流逝一半的時候,x的變換應該是一般,即x的改變是0.5,為什麼呢?因為它是線性插值器,是實現勻速動畫的,下面看它的原始碼:
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

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

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}
很顯然,線性插值器的返回值和輸入值一樣,因此插值器返回的值是0.5,這意味著x改變的是0.5,這個時候插值器的工作就完成了。具體x變成了什麼值,這個需要估值演算法來確定,我們來看看整型估值演算法的原始碼。
public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>int</code> or
     *                   <code>Integer</code>
     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}
上述演算法很簡答,evaluate的三個引數分別表示估值小數、開始值和結束值,對應於我們的例子就分別是0.5、0、40。根據上述演算法,整型估值返回給我的結果就是20,這就是(x=20,t=20ms)的由來。屬性動畫要求物件的該屬性由set方法和get方法(可選)。插值器和估值演算法除了系統提供的外,我們還可以自定義。實現方式也很簡單,因為插值器和估值演算法都是一個介面,且內部只有一個方法,我們只要派生一個類實現介面就可以了,然後就可以做出千奇百怪的動畫效果了。具體一點就是:自定義插值器需要實現Interpolator或者TimeInterpolator,自定義估值演算法需要實現TypeEvaluator。另外就是如果要對其他型別(非int、float、Color)做動畫,那麼必須要自定義型別估值演算法。

3.屬性動畫的監聽器

屬性動畫提供了監聽器用於監聽動畫的播放過程,主要有如下兩個介面:AnimatorUpdateListener和AnimatorListener。AnimatorListener的定義如下: public static interface AnimationListener { void onAnimationStart(Animator animation); void onAnimationEnd(Animator animation); void onAnimationRepeat(Animator animation);         void onAnimationCancel(Animator animation); }從AnimatorListen可以看出,它可以監聽動畫的開始、結束以及重複播放。同時為了方便開發,系統還提供了AnimatorListenerAdapter這個類,他是AnimatorListener的介面卡,這樣我們就可以有選擇地實現上面的4個方法了,畢竟不是所有方法都是我們感興趣的。下面再看一下AnimatorUpdateListener的定義,如下所示。
    public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation);
    }
AnimatorUpdateListener比較特殊,它會監聽整個動畫過程,動畫是由許多幀組成的,沒播放一幀,onAnimationUpdate就會被呼叫一次,利用這個特性,我們可以做一些特殊的事情。

4. 對任意屬性做動畫

這裡先提出一個問題:給Button加一個動畫,讓這個Button的寬度從當前寬度增加到500px。也許你會說,這很簡單,用View動畫就可以搞定,我們可以來試試,你能寫出來嗎? 很快你就會恍然大悟,原來View動畫根本就不支援對寬度進行動畫。沒錯,View動畫只支援四種類型:平移、旋轉、縮放、不透明度。當x方向的縮放可以讓Buttonx方向方法,看起來好像是寬度增加了,實際上不是,只是Button被放大了而已,而且由於只是x方向被放大,這個時候Button的背景以及上面的文字都被拉伸了,甚至由可能Button會超出螢幕。這樣的的效果顯然是很差的,而且也不是真正地對寬度做動畫。不過索性我們還有屬性動畫,我們用屬性動畫試試,如下所示。
    private void performAnimate() {
        ObjectAnimator.ofInt(mButton,"width",500).setDuration(5000).start();
    }

    @Override
    public void onClick(View v) {
        if (v == mButton){
            performAnimate();
        }
    }
上述程式碼執行後發現沒效果,其實沒效果是對的,如果隨便傳遞一個屬性過去,輕則沒有動畫效果,重則程式直接Crash。下面分析屬性動畫的原理:屬性動畫要求動畫作用的物件提供該屬性的set和get方法,屬性動畫根據外界傳遞的該屬性的初始值和最終值,以動畫的效果多次去呼叫set方法,每次傳遞給set方法的值都不一樣,確切來說是隨著時間的推移,所傳遞的值越來越接近最終值。總結一下,我們對object的屬性abc做動畫,如果想讓動畫生效,需要同時滿足兩個條件:
(1)object必須要提供setAbc方法,如果動畫的時候沒有傳遞初始值,那麼還要提供getAbc方法,因為系統要去取abc屬性的初始值(如果這條不滿足,程式直接Crash);(2)object的setAbc對屬性abc所做的改變必須能夠通過某種方法反映出來,比如會帶來UI的改變之類的(如果這條不滿足,動畫無效果但不會Crash)。

以上條件缺一不可。那麼為什麼我們對Button的width屬性做動畫會沒有效果?這時因為Button內部雖然提供了getWidth和setWidth,但是這個setWidth方法並不是改變檢視的大小,他是TextView新新增的方法,View是沒有這個setWidth方法的,由於Button繼承了TextView,所以Button也就由了setWidth方法。下面看一下這個getWidth和setWidth方法的原始碼。

    @android.view.RemotableViewMethod
    public void setWidth(int pixels) {
        mMaxWidth = mMinWidth = pixels;
        mMaxWidthMode = mMinWidthMode = PIXELS;

        requestLayout();
        invalidate();
    }
    
    @ViewDebug.ExportedProperty(category = "layout")
    public final int getWidth() {
        return mRight - mLeft;
    }
從上述原始碼可以看出,getWidth的確是獲取View的寬度的,而setWidth是TextView和其子類的專屬方法,它的作用不是設定View的寬度,而是設定TextView的最大寬度和最小寬度的,這個和TextView的寬度不是一個東西。具體來說,TextView的寬度對於XML中的android:layout_width屬性,而TextView還有一個屬性android:width,這個android:width屬性就對應了TextView的setWidth。總之,TextView和Button的setWidth、getWidth乾的不是同一件事情,通過setWidth無法改變空間的寬度,所以對width做屬性動畫沒有效果。對應於動畫的兩個條件來說,本例中動畫不生效的原因是滿足了條件1而未滿足條件2.針對上述問題,官方文件告訴我們有三種解決方法:
  • 給你物件加上get和set方法,如果你有許可權的話;
  • 用一個類來包裝原始物件,間接為其提供get個set方法;
  • 採用ValueAnimator,監聽動畫過程,自己實現屬性的改變。
針對上面提出的三種解決辦法,下面給出具體的介紹。1.給你的物件加上get和set方法,如果你有許可權的話這個意思很好理解,如果你有許可權的話,加上get和set就搞定了。但是很多時候我們沒許可權去這麼做。比如文字開頭所提到的問題,你無法給Button加上一個合乎要求的setWidth方法,因為這時Android SDK內部實現的。這個方法最簡單,但是往往不可行的,這裡就不對其進行更多的分析了。2.用一個類來包裝原始物件,間接為其提供get和set方法這是一個很有用的解決辦法,是筆者最喜歡用的,因為用起來很方便,也很好理解,下面將通過一個具體的例子來介紹它。
    private void performAnimate() {
        ViewWrapper wrapper = new ViewWrapper(mButton);
        ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
    }

    @Override
    public void onClick(View v) {
        if (v == mButton){
            performAnimate();
        }
    }

    private static class ViewWrapper {
        private View mTarget;

        public ViewWrapper(View target) {
            this.mTarget = target;
        }
        public int getWidth() {
            return mTarget.getLayoutParams().width;
        }
        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }
上述程式碼再5s內讓Button的寬度增加到了500px,為了達到這個效果,我們提供了ViewWrapper類專門用於包裝View,具體到本例是包裝Button。然後我們對ViewWrapper的width屬性做動畫,並且setWidth方法中修改器內部的target的寬度,而target實際上就是我們包裝的Button。這樣一個間接屬性動畫就搞定了,上述程式碼同樣適用於一個物件的其他屬性。3.採用ValueAnimator,監聽動畫過程,自己實現屬性的改變首先說說什麼是ValueAnimatorValueAnimator本身不做用於任何物件,也就是說直接使用它沒有任何動畫效果。它可以對一個值做動畫,然後我們可以監聽其動畫過程,再動畫過程中修改我們的物件的屬性值,這樣也就相當於我們的物件做了動畫。下面用例子來說明:
    private void performAnimate(final View target, final int start, final int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            //持有一個IntEvaluator物件,下面方便估值的時候使用
            private IntEvaluator mEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                //獲得當前動畫的進度值,整數,1~100之間
                int currentValue = (Integer)animator.getAnimatedValue();
                Log.d(TAG, "current value: " + currentValue);

                //獲得當前進度棧整個動畫過程的比例,浮點型,0~1之間
                float fraction = animator.getAnimatedFraction();

                //直接用用整型估值器,通過比例計算出寬度,然後再設給Button
                target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
                target.requestLayout();
            }
        });
        valueAnimator.setDuration(5000).start();
    }

    @Override
    public void onClick(View v) {
        if (v == mButton){
            performAnimate(mButton, mButton.getWidth(), 500);
        }
    }
上述程式碼的效果圖和採用ViewWrapper是一樣的。關於這個ValueAnimator要再說一下,拿上面的例子來說,它會再5000ms內將一個數從1變到100,然後動畫的每一幀會回撥onAnimationUpdate方法。再這個方法裡,我們可以獲取當前的值(1~100)和當前值所佔的比例,我們可以計算出Button現在的寬度應該是多少。比如時間過了一般,當前值是50,比例為0.5,假設Button的起始寬度是100px,最終寬度是500px,那麼Button增加的寬度也應該佔總增加寬度的一半,總增加寬度是500-100=400,所以這個時候Button應該增加的寬度是400*0.5=200,那麼當前Button的寬度應該為初始寬度+增加寬度(100+200=300)。上述計算過程很簡單,其實他就是整型估值器IntEvaluator,所以我們不用自己寫了,直接用吧。

5.屬性動畫的工作原理

屬性動畫要求動畫作用的物件提供該屬性的set方法,屬性動畫根據你傳遞的該屬性的初始值和最終值,以動畫的效果多次去呼叫set方法。每次傳遞set方法的值都不一樣,確切來說是隨著時間的推薦,所以傳遞的值越來越接近最終值。如果動畫的時候沒有傳遞初始值,那麼還要提供get方法,因為系統要去獲取屬性的初始值。對於屬性動畫來說,啟動話過程中所做的就是這麼多,下面看原始碼分析。首先我們要找一個入口,就從ObjectAnimator.ofInt(mButton,"width",500).setDuration(5000).start(),其他動畫都是類似的。先看ObjectAnimator的start方法:
    @Override
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        if (DBG) {
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();
    }
上面程式碼其實做的事情很簡單,首先會判斷如果當前動畫、等待的動畫(Pending)和延遲的動畫(Delay)中有當前動畫相同的動畫,那麼就把相同的動畫給取消掉,接下那一段是log,再接著就是呼叫了弗雷的super.star()方法。因為ObjectAnimator繼承了ValueAnimator,所以接下來我們看一下ValueAnimator的Start方法:
    private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }
可以看出屬性動畫需要執行再Lopper的執行緒中。上述程式碼最終會呼叫AnimationHandler的start方法,這個AnimationHandler並不是Handler,他是一個Runnable。看一下他的程式碼,通過程式碼我們發現,很快就調到JNI層,不過JNI層最終還是要調回來的。它的run方法會被呼叫,這個Runnable涉及和底層的互動,這麼就忽略這部分,直接看重點:ValueAnimator中的doAnimationonFrame方法,如下所示。
    public final boolean doAnimationFrame(long frameTime) {
        if (mStartTime < 0) {
            // First frame. If there is start delay, start delay count down will happen *after* this
            // frame.
            mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);
        }

        // Handle pause/resume
        if (mPaused) {
            mPauseTime = frameTime;
            removeAnimationCallback();
            return false;
        } else if (mResumed) {
            mResumed = false;
            if (mPauseTime > 0) {
                // Offset by the duration that the animation was paused
                mStartTime += (frameTime - mPauseTime);
            }
        }

        if (!mRunning) {
            // If not running, that means the animation is in the start delay phase of a forward
            // running animation. In the case of reversing, we want to run start delay in the end.
            if (mStartTime > frameTime && mSeekFraction == -1) {
                // This is when no seek fraction is set during start delay. If developers change the
                // seek fraction during the delay, animation will start from the seeked position
                // right away.
                return false;
            } else {
                // If mRunning is not set by now, that means non-zero start delay,
                // no seeking, not reversing. At this point, start delay has passed.
                mRunning = true;
                startAnimation();
            }
        }

        if (mLastFrameTime < 0) {
            if (mSeekFraction >= 0) {
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
            }
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        mLastFrameTime = frameTime;
        // The frame time might be before the start time during the first frame of
        // an animation.  The "current time" must always be on or after the start
        // time to avoid animating frames at negative time intervals.  In practice, this
        // is very rare and only happens when seeking backwards.
        final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            endAnimation();
        }
        return finished;
    }
注意上述程式碼,末尾呼叫了animateBaseOnTime方法,而animateBaseOnTime內部呼叫了animateValue,下面看animateValue的程式碼:
    @CallSuper
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }
上述程式碼中的calculateValue方法就是計算每幀動畫所應用的屬性的值,下面著重看一下到底是再哪裡呼叫屬性的get和set方法的,畢竟這個才是我們最關心的。在初始化的時候,如果屬性的初始值沒有提供,則get方法會被呼叫,情況PropertyValuesHolder的setupValue方法,可以發現get方法是通過反射來呼叫的,如下所示。
    private void setupValue(Object target, Keyframe kf) {
        if (mProperty != null) {
            kf.setValue(mProperty.get(target));
        }
        try {
            if (mGetter == null) {
                Class targetClass = target.getClass();
                setupGetter(targetClass);
                if (mGetter == null) {
                    // Already logged the error - just return to avoid NPE
                    return;
                }
            }
            kf.setValue(mGetter.invoke(target));
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
當動畫的下一幀到來的時候,PropertyValuesHolder中的setAnimatedValue方法會將新的屬性值設定給物件,呼叫其set方法。從下面的原始碼可以看出,set方法也是通過反射來呼叫的:
    void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString());
            }
        }
    }

相關推薦

Android動畫深入分析——屬性動畫

屬性動畫是在API11中引入的特性,和View動畫不同,它對作用物件進行了擴充套件,屬性動畫可以對任何物件做動畫,甚至還可以沒有物件。除了作用物件進行擴充套件以外,屬性動畫的效果也得到了加強,不再像View動畫那樣只能支援四種簡單的變換。屬性動畫中有ValueAnimator

Android屬性動畫深入分析 讓你成為動畫牛人

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android屬性動畫深入分析:讓你成為動畫牛人

轉載請註明出處:http://blog.csdn.net/singwhatiwanna/article/details/17841165 前言 感謝你閱讀本文,我堅信讀完本文肯定不會讓你失望的。想要做動畫牛人?想要精通動畫?那麼本文所講的內容都是你必須要掌握的。關於動畫,

Android動畫深入分析

Android的動畫可以分為三種: View動畫、幀動畫、屬性動畫。其實幀動畫也屬於View動畫,只不過它和平移、旋轉等常見的View動畫在表現形式上略有不同而已。View動畫通過對場景裡的物件的不斷做影象變換(平移、縮放、旋轉、透明度)從而產生動畫效果,它是一種漸進式動畫,並且View

Android圖文具體解釋屬性動畫

ref processor 拷貝 number name rup prop ren del Android中的動畫分為視圖動畫(View Animation)、屬性動畫(Property Animation)以及Drawable動畫。從Android 3

Android三種動畫之(三)屬性動畫

 分享自Carson_Ho的簡書,這篇寫的很詳細,我就不浪費時間寫了 目錄 目錄 1. 屬性動畫出現的原因 屬性動畫(Property Animation)是在 Android 3.0(API 11)後才提供的一種全新動畫模式 那麼為什麼要提供屬性動畫

Android學習筆記】屬性動畫基礎學習筆記

屬性動畫 屬性動畫系統是一個具有魯棒性的框架,允許你幾乎讓一切都動起來。你能夠定義一個動畫來隨著時間改變任何物件的任何屬性,無視該物件是否是畫在在螢幕上的。屬性動畫在指定的時間內改變屬性值(某個物件的某個屬性)。為了讓目標動起來,需要特別指明所要運動的目標的屬性,例如目標在螢幕上的位置,運動的時間長

Android中的動畫和原理(屬性動畫)

1、屬性動畫 屬性動畫通過改變物件的屬性來展示的動畫效果,補間動畫只是設定當前View在區域內移動,產生的動畫效果,其實原View的還在原地,沒有發生改變。 但屬性動畫改變了物件的屬性。也就是改變了物件的顏色,位置,寬高等。 2、示例 publi

Android動畫系列之屬性動畫

> 原文首發於微信公眾號:jzman-blog,歡迎關注交流! 屬性動畫相較幀動畫和補間動畫更強大,幀動畫和補間動畫只能應用於 View 及其子類,而屬性動畫可以修改任何物件的屬性值,屬性值可在指定的一段時間內自動改變,根據物件屬性值的變化進而實現更復雜的動畫。 1. 屬性動畫的常用設定 2. Va

Android Gson深入分析

目前解析json有三種工具:org.json(Java常用的解析),fastjson(阿里巴巴工程師開發的),Gson(Google官網出的),解析速度最快的是Gson,下載地址:https://code.google.com/p/google-gson/什麼是JSON:JS

Android FrameWork深入分析DreamManagerService實現自己的系統屏保

Framework分析DreamService,實現自己的系統屏保 現在有一個需求是在手機一段時間不用的情況下,顯示自己的系統屏保功能 下面是涉及到的程式碼 /frameworks/base/services/core/java/com/android/

android ProgressBar 深入分析

ProgressBar 既進度條,當我們在做一些耗時操作的時候(例如下載檔案),可以使用 ProgressBar 給使用者提供一個進度提示,告訴使用者當前的進度。ProgressBar 提供了兩種進度顯示模式,分別是具有進度值的精確模式和不具有進度值的模糊模式。本文將分別從 ProgressBar

Android 屬性動畫原始碼分析 && Handler Epoll

根據屬性和Value 設定關鍵幀,然後通過AnimationHandler 呼叫 Chroeographor 去監聽VSYNC 訊號,收到Vsync訊號後,呼叫doFrame,最終回撥到AnimationHandler 設定當前時間 對應的 value。 https://www.jianshu.

Android屬性動畫完全解析(上),初識屬性動畫的基本用法

fcm 操作 fad 擴展性 改變 內部使用 如果 轉載 @override 轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/43536355 在手機上去實現一些動畫效果算是件比較炫酷的事情,因此Andr

Android 屬性動畫(Property Animation) 全然解析 (上)

顏色 valid 全部 加速度 ext target ng- 點擊 save 轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/380674751、概述Android提供了幾種動畫類型:View Anima

android動畫具體解釋二 屬性動畫原理

api概述 告訴 繪制 限制 post 了解 播放 語言 targe property動畫是一個強大的框架,它差點兒能使你動畫不論什麽東西。你能夠定義一個動畫來改變對象的不論什麽屬性,不論其是否被繪制於屏幕之上。一個屬性動畫在一定時間內多次改變一個屬性(對象的一

淺談Android屬性動畫.

1、前言 在我們體驗一款APP時,炫酷的動畫往往能讓使用者體驗大幅度提升。想當年我剛學Android的時候,無意中看到蘑菇街購物車的動畫效果,把我給激動得,非要在自己的APP中加入那動畫,記得當時用費了好大的勁...不提了,說多了都是淚... 先了解下,目前可以實現動畫的方

android屬性動畫 ObjectionAnimation

   關於動畫:  android中動畫包括 View Animation、Drawable Animation和屬性動畫Property Animation      ●

Android 自定義柱狀圖及屬性動畫

前段時間公司專案中用到了統計圖,網上找了些資料和框架都不能滿足我的需求,沒辦法,只有自己寫了。 近來清閒,將其抽出一個demo了,歡迎大家交流指正。 效果圖先行 實現方案有兩個,一是自定義控制元件,二是使用屬性動畫。屬性動畫在api11以上版本才有,在11版本以下使用可以引入nin

Android動畫(二)-屬性動畫

概述 上一篇主要介紹了ViewAnim和幀動畫,篇幅有點長,另起一篇。上篇介紹的兩種動畫開發中用到的不多,主要還是本篇的屬性動畫使用比較廣。 1 補間動畫 1.1 Property Anim 開發中Property Anim使用比View Anim要更為廣泛,主要還是出於剛剛