View补间动画Animation运行原理

先上大料:

View 动画 Animation 运行原理解析

Android动画原理分析

看完以上两篇文章,基本上就可以理清补间动画的实现原理了.

基本流程如下:

1,使用View.startAnimation()开启一个动画,在这个方法里面初始化了animation,并且执行了invalidate方法,这个是进入视图绘制流程的起点.

2,一旦执行了view的invalidate,那么就进入了整个视图树的遍历,最终走到ViewRootImpl,执行performTraversals方法,这个方法又会去执行子视图的三大绘制流程.

3,其中在view的draw(三个参数)方法中,会执行到view的applyLegacyAnimation方法.

4,view的applyLegacyAnimation中,使用view的animation变量,执行animation的getTransformation(其中一个参数是父view的transformation变量)方法.

5,animation的getTransformation主要做两个事情,传进一个父view的childTransformation视图变换矩阵(实际上是子view的,只是不知道为什么要存放在父view中),得到一个动画是否完成的boolean返回值. 在这个方法里面执行了Animation的applyTransformation方法,这个方法具体下沉到子类中来实现,比如在子类ScaleAnimation中实现是这样的:

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

主要是根据第一个参数插值计算器计算出来的当前动画执行的进度,改变第二个参数transformation的值,记得这个值是父view的成员变量.这个值的具体用法,其实是用来和view的bitmap(是canvas)进行矩阵运算得到一个新的画布局域,也就是最终会根据这个运算得到一个新的视图,这也就是动画的真正原理,改变canvas画布里的bitmap(错误).https://blog.csdn.net/startfromweb/article/details/7644405

6,接着上面的计算,如果动画还要继续,那么getTransformation会返回true,回到view的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;
        }

        if (more) { // 这个值为true的话
            if (!a.willChangeBounds()) {
                if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
                        ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                    parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
                } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                    // The child need to draw an animation, potentially offscreen, so
                    // make sure we do not cancel invalidate requests
                    parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    parent.invalidate(mLeft, mTop, mRight, mBottom);
                }
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                        invalidationTransform);

                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f)); // 又重新执行了invalidate
            }
        }
        return more;
    }

也就是当为true的情况,又会执行view的invalidate方法,那么就又进入了从1开始的流程中.这也就是为什么只启动一次,动画却一直会执行,视图会一直被更新,直到完成动画为止的原因了.

其实这个过程中,也就是不断的执行view的invalidate,通过Choreograher不断的安排执行view的绘制流程,在view的draw方法中,计算view的canvas变换矩阵.

所以这也就解释了,为什么补间动画只是改变view的视觉,他本来也就是仅仅改变了view的canvas中的bitmap(区域)而已.

你可能感兴趣的:(基础知识,源码分析)