Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android動畫完全解析(一)

Android動畫完全解析(一)

編輯:關於Android編程

一、View動畫

這裡寫圖片描述

1、常見的4中View動畫:AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation

使用動畫的方式有兩種:一種是xml形式、另一種是java代碼。使用起來都比較簡單。還有一種AnimationSet,它是動畫集合,將幾種動畫合在一起使用,下面AnimationSet來寫動畫。

創建縮放/透明動畫


    //創建AnimationSet對象
    aSet = new AnimationSet(false);
    //創建動畫對象
    sAnim = new ScaleAnimation(1, 0.5f, 1, 0.5f);
    //設置動畫執行時間
    sAnim.setDuration(3000);
    //添加動畫到集合中
    aSet.addAnimation(sAnim);
    aAnim = new AlphaAnimation(1, 0.5f);
    aAnim.setDuration(3000);
    aSet.addAnimation(aAnim);

開始執行動畫


    aSet.start() ;
    btn_sys.setAnimation(aSet) ;

就這樣,完成了一個動畫集合的小例子,其它幾種動畫的使用方法類似。

2.View動畫的源碼分析

分析源碼之前,我們需要知道這4種動畫其實都是Animation的子類,而如果想要實現動畫效果,則必須重寫applyTransformation()方法,這點可以從這個方法的注釋可以看出。


     /**
     * Helper for getTransformation. Subclasses should implement this to apply
     * their transforms given an interpolation value.  Implementations of this
     * method should always replace the specified Transformation or document
     * they are doing otherwise.
     * 
     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
     *        after it has been run through the interpolation function.
     * @param t The Transformation object to fill in with the current
     *        transforms.
     */
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    }

OK,這裡我們分析ScaleAnimation,其它三個可以自行分析。

ScaleAnimation

首先,我們看下構造函數


    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
            float pivotX, float pivotY) {
        mResources = null;
        mFromX = fromX;
        mToX = toX;
        mFromY = fromY;
        mToY = toY;

        mPivotXType = ABSOLUTE;
        mPivotYType = ABSOLUTE;
        mPivotXValue = pivotX;
        mPivotYValue = pivotY;
        initializePivotPoint();
    }

源碼很簡單,只是將傳遞過來的變量賦值,然後調用initializePivotPoint()方法


    private void initializePivotPoint() {
        if (mPivotXType == ABSOLUTE) {
            mPivotX = mPivotXValue;
        }
        if (mPivotYType == ABSOLUTE) {
            mPivotY = mPivotYValue;
        }
    }

在構造函數執行完之後,馬上就會執行initialize(),獲取縮放中心點坐標


    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);

        mFromX = resolveScale(mFromX, mFromXType, mFromXData, width, parentWidth);
        mToX = resolveScale(mToX, mToXType, mToXData, width, parentWidth);
        mFromY = resolveScale(mFromY, mFromYType, mFromYData, height, parentHeight);
        mToY = resolveScale(mToY, mToYType, mToYData, height, parentHeight);

        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
    }

緊接著,當調用animation.start()方法,


    public void setStartTime(long startTimeMillis) {
        mStartTime = startTimeMillis;
        mStarted = mEnded = false;
        mCycleFlip = false;
        mRepeated = 0;
        mMore = true;
    }

    /**
     * Convenience method to start the animation the first time
     * {@link #getTransformation(long, Transformation)} is invoked.
     */
    public void start() {
        setStartTime(-1);
    }

這個只是開啟設置下時間,重要的是view.setAnimation()這個方法。如果上面的例子代碼不書寫view.setAnimation(),則不會出現動畫效果,而如果書寫.start()方法則依然可以執行,只不過再第二次執行時候的是在第一次執行的基礎上,不是我們想要的結果。看源碼


    /**
     * Sets the next animation to play for this view.
     * If you want the animation to play immediately, use
     * {@link #startAnimation(android.view.animation.Animation)} instead.
     * This method provides allows fine-grained
     * control over the start time and invalidation, but you
     * must make sure that 1) the animation has a start time set, and
     * 2) the view's parent (which controls animations on its children)
     * will be invalidated when the animation is supposed to
     * start.
     *
     * @param animation The next animation, or null.
     */
    public void setAnimation(Animation animation) {
        mCurrentAnimation = animation;

        if (animation != null) {
            // If the screen is off assume the animation start time is now instead of
            // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
            // would cause the animation to start when the screen turns back on
            if (mAttachInfo != null && !mAttachInfo.mScreenOn &&
                    animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
                animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
            }
            animation.reset();
        }
    }

從注釋中可以看出,和start方法調用的是同一個方法。接著執行的是applyTransformation()這個方法,此方法是自行實現的,而且沖該方法的注釋可以看出這個方法是是Helper for getTransformation.查看getTransformation()


    /**
     * Gets the transformation to apply at a specified point in time. Implementations of this
     * method should always replace the specified Transformation or document they are doing
     * otherwise.
     *
     * @param currentTime Where we are in the animation. This is wall clock time.
     * @param outTransformation A transformation object that is provided by the
     *        caller and will be filled in by the animation.
     * @return True if the animation is still running
     */
    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mStartTime == -1) {
            mStartTime = currentTime;
        }

        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }

        final boolean expired = normalizedTime >= 1.0f;
        mMore = !expired;

        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            if (!mStarted) {
                fireAnimationStart();
                mStarted = true;
                if (USE_CLOSEGUARD) {
                    guard.open("cancel or detach or getTransformation");
                }
            }

            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

            if (mCycleFlip) {
                normalizedTime = 1.0f - normalizedTime;
            }

            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation);
        }

        if (expired) {
            if (mRepeatCount == mRepeated) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }

        if (!mMore && mOneMoreTime) {
            mOneMoreTime = false;
            return true;
        }

        return mMore;
    }

這個方法中有一個特別重要的代碼: applyTransformation(interpolatedTime, outTransformation);故它會調用applyTransformation方法來通過矩陣實現變換。

上面整體分析了Animation的執行流程,現在就具體來分析下ScaleAnimation是怎麼做到縮放的。其實整個縮放動畫一共就不到300行代碼,而真正起決定作用的又只有幾十行代碼。


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float sx = 1.0f;
        float sy = 1.0f;
        float scale = getScaleFactor();

        if (mFromX != 1.0f || mToX != 1.0f) {
            sx = mFromX + ((mToX - mFromX) * interpolatedTime);
        }
        if (mFromY != 1.0f || mToY != 1.0f) {
            sy = mFromY + ((mToY - mFromY) * interpolatedTime);
        }

        if (mPivotX == 0 && mPivotY == 0) {
            t.getMatrix().setScale(sx, sy);
        } else {
            t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
        }
    }

由源碼可知最後動畫還是通過矩陣變換來實現的。這裡的interpolatedTime表示差值器,這個概念後面會提到,首先,獲取縮放比例,然後,再根據不同時間段獲取不同的sx值,最後通過矩陣變換來設置。 其實這個方法是會和前面提到過的getTransformation()這個方法一起執行起作用的,兩個方法一直執行都動畫結束。下面將會寫一個Demo來演示這點。

3.自定義一個動畫

前面使用AnimationSet將ScaleAnimation和AlphaAnimation結合起來,那麼我們可不可以自定義一個Animation來實現這個效果呢?答案是肯定的。

a.首先,繼承Animation


    public class ScaleAndAlphaAnimation extends Animation

b.接著就是利用構造函數將需要的參數傳遞進來


    public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){
        this.mFromX = fromX ;
        this.mFromY = fromY ;
        this.mToX = toX; 
        this.mToY = toY ;
        this.mFromAlpha = fromAlpha ;
        this.mToAlpha = toAlpha ;
        System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
    }

c.最後就是復寫applyTransformation方法了,這裡我是參照ScaleAnimation和AlphaAnimation源碼來寫的


    @SuppressLint("NewApi") @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        //縮放動畫設置
        float sx = 1.0f;
        float sy = 1.0f;
        float scale = getScaleFactor();
        if (mFromX != 1.0f || mToX != 1.0f) {
            sx = mFromX + ((mToX - mFromX) * interpolatedTime);
        }
        if (mFromY != 1.0f || mToY != 1.0f) {
            sy = mFromY + ((mToY - mFromY) * interpolatedTime);
        }
        t.getMatrix().setScale(sx, sy);
        //透明度動畫設置
        final float alpha = mFromAlpha;
        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
        System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
        //        這在scaleanimation源碼中代表縮放中心掉的位置
        //      if (mPivotX == 0 && mPivotY == 0) {
        //            t.getMatrix().setScale(sx, sy);
        //         } 
        //        else {
        //            t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
        //         }
                }

全部代碼



    public class ScaleAndAlphaAnimation extends Animation{

        private float mFromX ;
        private float mFromY ;
        private float mToX ;
        private float mToY ;
        private float mFromAlpha ;
        private float mToAlpha ;
        //縮放比例
        private float scale = 1 ;

        public ScaleAndAlphaAnimation(float fromX, float toX, float fromY, float toY,float fromAlpha, float toAlpha){
            this.mFromX = fromX ;
            this.mFromY = fromY ;
            this.mToX = toX; 
            this.mToY = toY ;
            this.mFromAlpha = fromAlpha ;
            this.mToAlpha = toAlpha ;
            System.out.println("ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()");
        }

        @SuppressLint("NewApi") @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            //縮放動畫設置
            float sx = 1.0f;
            float sy = 1.0f;
            float scale = getScaleFactor();
            if (mFromX != 1.0f || mToX != 1.0f) {
                sx = mFromX + ((mToX - mFromX) * interpolatedTime);
            }
            if (mFromY != 1.0f || mToY != 1.0f) {
                sy = mFromY + ((mToY - mFromY) * interpolatedTime);
            }
            t.getMatrix().setScale(sx, sy);
            //透明度動畫設置
            final float alpha = mFromAlpha;
            t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
            System.out.println("ScaleAndAlphaAnimation.applyTransformation()");
    //        這在scaleanimation源碼中代表縮放中心掉的位置
    //      if (mPivotX == 0 && mPivotY == 0) {
    //            t.getMatrix().setScale(sx, sy);
    //         } 
    //        else {
    //            t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
    //         }
            }


        @Override
        public boolean getTransformation(long currentTime,
                Transformation outTransformation) {
            System.out.println("ScaleAndAlphaAnimation.getTransformation()");
            return super.getTransformation(currentTime, outTransformation);
        }

        @SuppressLint("NewApi") @Override
        protected float getScaleFactor() {
            return 0.5f;
        }


        @Override
        public void initialize(int width, int height, int parentWidth,
                int parentHeight) {
            super.initialize(width, height, parentWidth, parentHeight);
            System.out.println("ScaleAndAlphaAnimation.initialize()");
        }
    }

log輸出如下:從這裡可以看出,這幾個方法執行的順序是
構造函數–>initialize()–>接著就是applyTransformation()和getTransformation()的重復執行到動畫結束了。(先applyTransformation()後getTransformation())


    07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.ScaleAndAlphaAnimation()
    07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.initialize()
    07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
    07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
    07-12 06:42:57.958: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
    07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
    07-12 06:42:57.968: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
    07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()
    07-12 06:42:57.998: I/System.out(4992): ScaleAndAlphaAnimation.applyTransformation()
    07-12 06:42:58.028: I/System.out(4992): ScaleAndAlphaAnimation.getTransformation()

OK,這篇就介紹到這裡,下篇繼續分析動畫。

源碼將會在下篇一起給。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved