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

一.概述

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

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

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

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

从源码的角度分析Android动画运行原理_第1张图片

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

你可能感兴趣的:(从源码的角度分析Android动画运行原理)