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

源码将会在下篇一起给。

时间: 2024-07-30 14:35:26

Android动画完全解析(一)的相关文章

Android 动画深入解析

动画在安卓的一些娱乐应用上面应用非常广泛,在不牺牲性能的情况下,可以带来很好的体验,下面就详细讲解一下安卓动画的实现方式.学知识就学个明明白白. 动画类型 Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 JavaCode中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 Transla

android动画深入解析--仿58底部导航的item动画

我们在开发android的过程中,合理使用动画能够提高用户体验,带给用户耳目一新的感觉.因此我们应该掌握android的动画使用.我在开发的过程中,很少自己写动画,在github搜基本可以满足要求,但是本着打破砂锅问到底的小强精神,知其然更要知其所以然,我决定还是好好看看文档,深入的学习一下动画吧,我始终相信在复杂的动画也是有简单的动画构成的,学好基本功.从简单到复杂. 首先我去看看了官方的文档,大概要学的东西就是这些. AccelerateDecelerateInterpolator Acce

Android动画完全解析--属性动画

一.概述 上篇博客介绍了View动画的简单使用和基本工作原理原理,这篇来学习下属性动画.和View动画不同的是,属性动画不再简单的使用平移.旋转.缩放.透明度这4种变换,代替它们的是ValueAnimator.ObjectAnimator等概念. 二.运行截图 三.TimeInterpolator和TypeEvaluator 在真正学习属性动画之前,我们需要理解TimeInterpolator和TypeEvaluator这两个概念. 1.TimeInterpolator 中文翻译为时间插值器.它

Android开发之Tween(补间动画)完全解析(下)

欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章:Android开发之Tween(补间动画)完全解析(上),这篇文章将从代码的角度实现上篇文章的效果.如有疑问请留言,如有谬误欢迎批评指正. T

Android属性动画完全解析

Android属性动画完全解析(上),初识属性动画的基本用法 Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法 Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法

Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中我们学习了属性动画的一些进阶技巧,包括ValueAnimator和ObjectAnimator的高级用法,那么除了这些之外,当然还有一些其它的高级技巧在等着我们学习,因此本篇文章就对整个属性动画完全解析系列收个尾,来学习一下剩下的非常重要的高级技巧. 另外,本篇文章中使用的代码是建立在上篇文章基础之

Android动画解析--XML

动画类型 Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 JavaCode中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 TranslateAnimation 画面转换位置移动动画效果 RotateAnimation 画面转移旋转动画效果 Android动画模式 Animation

Android属性动画完全解析(上)

Android属性动画完全解析(上) 转载:http://blog.csdn.net/guolin_blog/article/details/43536355 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation).逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似

(转)Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法

目录(?)[-] Interpolator的用法 ViewPropertyAnimator的用法 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中我们学习了属性动画的一些进阶技巧,包括ValueAnimator和ObjectAnimator的高级用法,那么除了这些之外,当然还有一些其它的高级技巧在等着我们学习,因此本篇文章就对整个属性动画完全解析