View动画执行过程和插值器作用

示例

博客
补间动画的使用很简单,如下面代码,让图片旋转360度:

        animation = new RotateAnimation(0,360);
        animation.setDuration(3000);
        iv.startAnimation(animation);

那么补间动画说怎么执行的,插值器又是怎么用上的能?

动画的启动 View

从动画启动开始吧,看View的startAnimation方法:

/frameworks/base/core/java/android/view/View.java:

    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        setAnimation(animation);
        invalidateParentCaches();
        invalidate(true);
    }
    public void setAnimation(Animation animation) {
        mCurrentAnimation = animation;
        if (animation != null) {
            ......
        }
    }

这里面没几行代码,首先把动画设置给内部的一个变量,然后调用invalidate(true)方法。

这个方法会引起View的draw()方法的执行,并且是整个View的重绘。

/frameworks/base/core/java/android/view/View.java:

public Animation getAnimation() {
        return mCurrentAnimation;
    }
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        Transformation transformToApply = null;
        boolean concatMatrix = false;
        //拿到动画
        final Animation a = getAnimation();
        //动画不为空,说明有动画要执行
        if (a != null) {
            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            //获取Transformation,里面包换透明度和矩阵,这个Transformation的值在上面已经设置好了
            transformToApply = parent.getChildTransformation();
        } else {
        }
        //根据Transformation进行绘制。
        return more;
    }

调用本类的applyLegacyAnimation方法。

/frameworks/base/core/java/android/view/View.java:

private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
            Animation a, boolean scalingRequired) {
        Transformation invalidationTransform;
        final int flags = parent.mGroupFlags;
        final boolean initialized = a.isInitialized();
        if (!initialized) {
            //初始化动画
            a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
            a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
            if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
            onAnimationStart();
        }
        //注意这里也是通过这个方法拿到一个Transformation。
        final Transformation t = parent.getChildTransformation();
        //获取动画的变化值,并返回是否还有下一帧。
        boolean more = a.getTransformation(drawingTime, t, 1f);
        if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
            if (parent.mInvalidationTransformation == null) {
                parent.mInvalidationTransformation = new Transformation();
            }
            invalidationTransform = parent.mInvalidationTransformation;
            a.getTransformation(drawingTime, invalidationTransform, 1f);
        } else {
            invalidationTransform = t;
        }
        if (more) {
            //如果有下一帧,就继续刷新绘制
        }
        return more;
    }

动画的动画值计算设置

/frameworks/base/core/java/android/view/animation/Animation.java:

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;
        }
        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
        ......
        //通过标准时间,用插值器计算插值器转换之后的值
            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            //调用子类实现的方法,
            applyTransformation(interpolatedTime, outTransformation);
        }
        return mMore;
    }
protected void applyTransformation(float interpolatedTime, Transformation t) {
}

首先计算标准时间。标准时间是0到1的值,表示时间的进度。通过这个进度计算动画的进度。计算方法是,先用开始时间和延迟开始时间计算动画真正的开始时间,然后用当前时间减去动画真正开始的时间,算出动画已经运行的时间。用这个时间除以动画的总运行时间久得到当前动画的进度。

插值器

插值器改变的就是改变不同时间进度上的值,时间的流逝是线性的,速度是不变的,但是插值器通过改变不同时间上动画的值,达到控制动画的目的。

默认是加速加速插值器,里面是一个余弦曲线,随着标准时间从0到1,返回的数值是先加速再减速的,动画就会先变快在变慢,第一个效果图中看的很明显。

/frameworks/base/core/java/android/view/animation/AccelerateDecelerateInterpolator.java:

public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

下面可以看一下线性插值器,他就原原本本的返回了标准时间,不做任何改变,所以动画就会匀速执行:

/frameworks/base/core/java/android/view/animation/LinearInterpolator.java

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

Animation的子类实现applyTransformation方法

Animation的applyTransformation方法是一个空方法,需要子类去实现。下面看一啊AlphaAnimation的实现,现获取透明度总共要变化的值,然后通过传进来的插值器计算出的进度值,算出这个时间点上透明度应该是多少,然后设置给Transformation:

/Users/sunlinlin/Documents/AndroidSourcePart/frameworks/base/core/java/android/view/animation/AlphaAnimation.java

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float alpha = mFromAlpha;
        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
    }

AlphaAnimation里给传进来的Transformation设置了alpha值。Transformation主要有两个量,一个透明度,一个是矩阵。绘制时根据这个类里面存储的量来绘制,达到动画的效果。

例如旋转动画RotateAnimation,改变的就是矩阵,先算出动画一共要旋转的角度,然后根据插值器计算的进度值算出当前时间点上应该旋转到什么角度,然后设置给Transformation。

/frameworks/base/core/java/android/view/animation/RotateAnimation.java

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
        float scale = getScaleFactor();
        if (mPivotX == 0.0f && mPivotY == 0.0f) {
            t.getMatrix().setRotate(degrees);
        } else {
            t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
        }
    }

这时,回到上面View的draw()方法里,就会根据后面对Transformation的设置进行画面的绘制,一帧一帧的绘制就成了动画。

这也能解释为什么补间动画不会改变控件的真正位置了,因为这个动画只是重新对空间进行了draw,改变的只是看起来的样子,所以点击事件还得点击原来的地方。

简单的自定义插值器

插值器前面说的作用就是,在决定不同时间进度上的动画进度。时间进度是从0到1,而动画进度不一定非要从0到1.

比如,就已开始设置那个动画,3秒时间从0度旋转到360度,那么正常的他的动画进度就是:时间从0到3秒,角度从0到360.

动画的进度是可以再插值器中随便设置的,大于1也没可以。

下面是例子

自定义一个插值器

public class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        return 2*input;
    }
}

传进来的标准时间是0到1 ,返回的动画进度从0到2. 写的是转到360度,但是动画会从0转到720度,转两圈,速度是匀速。

        animation = new RotateAnimation(0,360);
        animation.setDuration(3000);
        animation.setInterpolator(new MyInterpolator());
        iv.startAnimation(animation);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容