Android动画完全解析--View动画

来源:互联网 发布:阿里云控制台中文乱码 编辑:程序博客网 时间:2024/06/07 14:12

一、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,这篇就介绍到这里,下篇继续分析动画。

源码将会在下篇一起给。

3 0
原创粉丝点击