从源码的角度分析Android动画运行原理

来源:互联网 发布:数控车编程软件 编辑:程序博客网 时间:2024/05/16 10:53

一.概述

首先说说动画的运行模式,一共有两种模式:

  • 独占模式,即程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束;
  • 中断模式,即有单独一个线程对时间计数,每隔一定的时间向主线程发通知,主线程接到通知后更新屏幕;

接下来我们看看动画的运行原理,一般情况下我们会调用使用如下的方式开启动画:

  AlphaAnimation animation = new AlphaAnimation(0,1f);        animation.setDuration(200);        animation.setFillAfter(true);        button.startAnimation(animation);

重点在startAnimation这个方法,这个方法是在哪定义的呢?是在所有控件的父类,View中,所以我们去View的源码中看看。

二.源码分析

public void startAnimation(Animation animation) {        animation.setStartTime(Animation.START_ON_FIRST_FRAME);        setAnimation(animation);        invalidateParentCaches();        invalidate(true);    }

首先给animation设置了一个起始时间START_ON_FIRST_FRAME,这个值是Animation类中定义的一个常量,值为-1,然后调用View中的setAnimation传入我们的动画对象,

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.mDisplayState == Display.STATE_OFF                    && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {                animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());            }            animation.reset();        }    }

在setAnimation方法里面,我们的animation对象会赋值给mCurrentAnimation这样一个全局变量,然后进行了判断,满足条件的话又重新设置了动画的时间,

public static long currentAnimationTimeMillis() {        return SystemClock.uptimeMillis();    }

这里返回了一个系统时间。然后调用reset方法重置动画为初始状态。
也就是说setAnimation主要做了两件事。第一件事就是设置动画起始时间,第二件事是重置动画状态。

最后调用invalidata方法重绘自己。

既然调用了setAnimation设置动画,那么应该有getAnimation来获取动画吧,最后我们在View的draw(Canvas canvas, ViewGroup parent, long drawingTime)里面发现了getAnimation这个方法。

  Transformation transformToApply = null;        boolean concatMatrix = false;        final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;        final Animation a = getAnimation();        if (a != null) {            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);            concatMatrix = a.willChangeTransformationMatrix();            if (concatMatrix) {                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;            }            transformToApply = parent.getChildTransformation();
public Animation getAnimation() {        return mCurrentAnimation;    }

这个getAnimation方法返回的正是我们定义的动画对象,因为我们之前在setAniamation方法中,将动画对象赋值给了这个全局变量。

接下来有调用了applyLegacyAnimation这个方法,

  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();        }        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;        }

我们只看重点代码,这里调用了动画对象的getTransformation方法

public boolean getTransformation(long currentTime, Transformation outTransformation,            float scale) {        mScaleFactor = scale;        return getTransformation(currentTime, outTransformation);    }

该方法直接调用了两个参数的方法

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);        }

该方法首先将当前时间currentTime处理成一个float类型的代表当前动画进度的对象。

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

然后将我们计算好的时间传入getInterpolation方法里面,获得一个插值器对应的时间,最后把这个时间和Transformation对象传入到applyTransformation方法中。

protected void applyTransformation(float interpolatedTime, Transformation t) {    }

我们发现applyTransformation这个方法的实现是空的,为何呢?因为不同的子类对这个方法进行了不同的实现。接下来我们看看

AlphaAnimation

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

ScaleAnimation

 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);        }    }

RotateAnimation

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);        }    }

TranslateAnimation

 protected void applyTransformation(float interpolatedTime, Transformation t) {        float dx = mFromXDelta;        float dy = mFromYDelta;        if (mFromXDelta != mToXDelta) {            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);        }        if (mFromYDelta != mToYDelta) {            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);        }        t.getMatrix().setTranslate(dx, dy);    }

可见applyTransformation()方法就是动画具体的实现,系统会以一个比较高的频率来调用这个方法,一般情况下60FPS,是一个非常流畅的画面了,也就是16ms,接下来我们验证一下这个时间。

 public void click(View vIew){        Animation animation = new Animation() {            private long last = 0;            @Override            protected void applyTransformation(float interpolatedTime, Transformation t) {                super.applyTransformation(interpolatedTime, t);                long curr = System.currentTimeMillis();                long time = curr-last;                last = curr;                System.out.println("===time: "+time);                t.getMatrix().postTranslate(10, 10);            }        };        animation.setDuration(500);        textView.startAnimation(animation);    }

这里写图片描述

可以看到时间间隔大概在20ms左右

0 0
原创粉丝点击