Android Animation动画原理源码分析

Android 平台提供了三类动画,一类是 Tween 动画-Animation,即通过对场景里的对象不断做图像变换 ( 平移、缩放、旋转 ) 产生动画效果;第二类是 Frame 动画,即顺序播放事先做好的图像,跟电影类似。最后一种就是3.0之后才出现的属性动画PropertyAnimator ,这个分享的是第一类动画原理。

Animation动画有4种,TranslateAnimation、ScaleAnimation、RotateAnimation、AlphAnimation,其都继承了Animation这个抽象类,实现了applyTransformation(float interpolatedTime, Transformation t)函数,如下ScaleAnimation的实现:

根据函数的命名来看就是对Transformation进行设置,通过传来interpolatedTime浮点值不断改变Transformation的矩阵Matrix来实现动画的缩放。那这个函数什么时候被调用,如何被调用,一步步来分析下:

可以看到在Animation中getTransformation(long currentTime, Transformation 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 || isCanceled();
    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 (NoImagePreloadHolder.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);
        //对transformation进行改变
        applyTransformation(interpolatedTime, outTransformation);
    }


    ******省略代码******


    if (!mMore && mOneMoreTime) {
        mOneMoreTime = false;
        return true;
    }


    return mMore;
}

那getTransformation函数又是在哪里被调用呢。我们一步步从动画api使用源头开始分析:
我们知道使用Animation执行动画从view.startAnimation(Animation animation)开始,其实现如下:

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

一看没有啥,就是设置赋值以及清父View Caches的标签设置,没有看出啥是如何让动画执行起来,调用了Animation的getTransformation函数。一般会说看到了invalidate函数调用了,就是刷新了View,那具体是怎么刷新View绘制的呢,那先讲下invalidate(true)执行流程和逻辑是啥,到底怎么样导致了刷新。没有别的办法,看源码,gogogo……详见我的另一遍文章《Android invalidate()和postInvalidate()刷新原理》

1、invalidate最后调用到invalidateInternal函数,把view的相对尺寸和相关状态设置传递

   void invalidate(boolean invalidateCache) {
    invalidateInternal(0, 0, mRight - mLeft, mBottom -  mTop, invalidateCache, true);
      }
 void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
        boolean fullInvalidate)

2、invalidateInternal函数中逻辑不少,主要部分如下,有段调用父view进行刷新:

        // Propagate the damage rectangle to the parent view.
     final AttachInfo ai = mAttachInfo;
     final ViewParent p = mParent;
     if (p != null && ai != null && l < r && t < b) {
     final Rect damage = ai.mTmpInvalRect;
     damage.set(l, t, r, b);
     p.invalidateChild(this, damage);
    }

那ViewParent.invalidateChild实现是在哪里呢,猜到ViewGroup,发现其实现了ViewParent的接口,并且在ViewGroup.addView时添加子View的逻辑中会最后调用到`addViewInner方法:

 private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

       ......

        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }

        if (mTransition != null) {
            mTransition.addChild(this, child);
        }

        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }

        if (preventRequestLayout) {
            child.mLayoutParams = params;
        } else {
            child.setLayoutParams(params);
        }

        if (index < 0) {
            index = mChildrenCount;
        }

        addInArray(child, index);

        // tell our children
        //设置父view
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }

        if (child.hasFocus()) {
            requestChildFocus(child, child.findFocus());
        }

     .....
    }

对child的ViewPrarent变量mParent进行赋值,把自己传递给子View,那么定位到ViewGroup对invalidateChild方法实现,发现里面有循环查找父View逻辑,如下:

do {
    View view = null;
    if (parent instanceof View) {
        view = (View) parent;
    }


   **********
         省略代码
    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
    **********
    parent = parent.invalidateChildInParent(location, dirty);
    if (view != null) {
        // Account for transform on current parent
       ********
    }
} while(parent!=null)

这个循环直到parent!=null才停止,那什么时候parent.invalidateChildInParent(location, dirty)会返回null呢。这里就要分析View树形结构了,布局结构中最上层的ViewParent是谁,什么时候赋值的。这个要从setContentView函数设置布局文件开始讲,有点长,但也许都是到布局的最顶层view就是DecorView(可以查源码),那DecorView的ViewParent又是谁,这个就必须从添加decorView定位。参考http://blog.csdn.net/luoshengyang/article/details/6689748这边老罗的文章,分析应用启动以及View显示加载过程,从中可以知道在AMS通知PerformResumeActivity命令时开始显示界面,会调用activity的makeVisible(),该函数添加了mDecor(DecorView)

 void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
 }

ViewManager.addView的实现在WindowManagerGlobal的addView方法中:

会看到一个ViewRootImpl,看起来很想最最顶层 的View,查找源码发现:并发View,但是其实现了ViewParent,这就差不多连起来了,然后其调用了setView方法查看实现如下

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;           

                ************
               很多代码省略
                *************
                view.assignParent(this);
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

                 ************
                 很多代码省略
                *************
            }
        }
    }

发现 mView = view进行赋值,并调用assignParent(this),把这个ViewParent实现付给DecorView,从持有父View。既然ViewRootImp实现了ViewParent,并且付给了DecorView,那在DecorView查找父parent时(parent.invalidateChildInParent(location, dirty))就可以定位到ViewRootImpl实现了,找到ViewRootImp.invalidateChildInParent:发现终于返回了null,结束了循环。

 @Override
 public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);


          if (mCurScrollY != 0 || mTranslator != null) {
            mTempRect.set(dirty);
            dirty = mTempRect;
            if (mCurScrollY != 0) {
                dirty.offset(0, -mCurScrollY);
            }
            if (mTranslator != null) {
                mTranslator.translateRectInAppWindowToScreen(dirty);
            }
            if (mAttachInfo.mScalingRequired) {
                dirty.inset(-1, -1);
            }
        }

        invalidateRectOnScreen(dirty);

        return null;
    }

invalidateRectOnScreen(dirty);又调用了谁,继续定位

可以查源码分析就不一步步写了,schuduleTraversals()调用是固定最后调用到performTraversals函数,这个是view绘制的源头开始处,代码很多,里面调用了mView.measure() mView.layout 、mView.draw等方法。终于差不多看到希望了,mView.draw()就是调用了DecorView的draw(),然后就把这个布局遍历绘制了一遍。那么走到执行动画的View的其父View绘制draw方法时候,会走到dispatchDraw,在View.draw(canvas)里面按步骤分别回执如下1、2、3、4、5、6步,其中有dispatchDraw来绘制子View。

public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
            (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;


    /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */

ViewGroup重载了dispatchDraw,实现绘制子View的内容,源码如下:

protected void dispatchDraw(Canvas canvas) {
    boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
    final int childrenCount = mChildrenCount;
    final View[] children = mChildren;
    int flags = mGroupFlags;
    //执行布局动画
    if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate(){
        final boolean buildCache = !isHardwareAccelerated();
        for (int i = 0; i < childrenCount; i++) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                final LayoutParams params = child.getLayoutParams();
                attachLayoutAnimationParameters(child, params, i, childrenCount);
                bindLayoutAnimation(child);
            }
        }
        final LayoutAnimationController controller = mLayoutAnimationController;
        if (controller.willOverlap()) {
            mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
        }
        controller.start();
        mGroupFlags &= ~FLAG_RUN_ANIMATION;
        mGroupFlags &= ~FLAG_ANIMATION_DONE;


        if (mAnimationListener != null) {
            mAnimationListener.onAnimationStart(controller.getAnimation());
        }
    }
     *******省略部分代码 *********
    // We will draw our child's animation, let's reset the flag


    //more为true表示动画没有执行完
    boolean more = false;
    final long drawingTime = getDrawingTime();
    *******省略部分代码 *********
    for (int i = 0; i < childrenCount; i++) {
         *******省略部分代码 *********
        int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
        final View child = (preorderedList == null) ? children[childIndex] :
                preorderedList.get(childIndex);
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            //绘制子View,直接调用了child.draw(canvas, this(ViewParent), drawingTime);
            // 动画真正执行地方
            more |= drawChild(canvas, child, drawingTime);
        }
    }


    *******省略部分代码 *********
    // mGroupFlags might have been updated by drawChild()
    flags = mGroupFlags;
    if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
        invalidate(true);
    }
        *******省略部分代码 *********
}

看到调用child.draw(Canvas canvas, ViewGroup parent, long drawingTime),这个draw(canvas)函数不一样,不过前者又会调用到后者。看下child.draw(Canvas canvas, ViewGroup parent, long drawingTime)是如何执行动画调用到animattion.applyTransformation()对Matrix进行矩阵变化,源码如下:

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {


    ****************省略部分代码 ***************
    boolean more = false;
    Transformation transformToApply = null;
    boolean concatMatrix = false;
    final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
    final Animation a = getAnimation();//获取ziView的动画,在View.startAnimation是就开始赋值了。
    if (a != null) {
        more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
        concatMatrix = a.willChangeTransformationMatrix();
        if (concatMatrix) {
            mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
        }
        transformToApply = parent.getChildTransformation();// a.applyLegacyAnimation进行赋值
    } else {
        ****************省略代码 ***************
    }
    ****************省略代码 ***************
    if (transformToApply != null || alpha < 1 || !
            hasIdentityMatrix() || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
        if (transformToApply != null || !childHasIdentityMatrix) {
            int transX = 0;
            int transY = 0;
            if (offsetForScroll) {
                transX = -sx;
                transY = -sy;
            }
            if (transformToApply != null) {
                if (concatMatrix) {
                    if (drawingWithRenderNode) {
                        renderNode.setAnimationMatrix(transformToApply.getMatrix());
                    } else {
                        // Undo the scroll translation, apply the transformation matrix, // then redo the scroll translate to get the correct result. 
                        canvas.concat(transformToApply.getMatrix());//传递给canvas进行矩阵实现动画
                        canvas.translate(transX, transY);
                    }
                    parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                }
                float transformAlpha = transformToApply.getAlpha();
                if (transformAlpha < 1) {
                    alpha *= transformAlpha;
                    parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                }
            }
            if (!childHasIdentityMatrix && !drawingWithRenderNode) {
                canvas.translate(-transX, -transY);
                canvas.concat(getMatrix());
                canvas.translate(transX, transY);
            }
        }
          ****************省略代码 ***************
        return more;
    }

在这么代码中动画animation判断不为空之后,进入more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);,这里面传入了animation,有没有对动画执行操作呢,如下源码:

private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
                                     Animation a, boolean scalingRequired) {
    Transformation invalidationTransform;
    *********省略部分代码 **********
    final Transformation t = parent.getChildTransformation();
    boolean more = a.getTransformation(drawingTime, t, 1f);///调用了Animation的
    ///getTransformation方法,这个方法进而调用了applyTransformation
    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又重新刷新,调用viewparent.invalidate
        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));//重新刷新
        }
    }
    return more;
}

从 view.applyLegacyAnimation()源码可以看到调用了animation.getChildTransformation()并通过返回的boolean值覆盖more变量,并且同时把对传入animation.getTransformation(drawingTime, t, 1f)中的transformToApply(parent.getChildTransformation()获取)进行变化矩阵,当more为true,看到又调用父View parent.invalidate(),重新刷新view树进行再一次绘制剩余动画内容。当applyLegacyAnimation执行完成之后draw(Canvas canvas, ViewGroup parent, long drawingTime)里面继续transformToApply = parent.getChildTransformation()获取变化之后transformToApply,若不为空,又传递给canvas.concat(transformToApply.getMatrix())来对canvas进行矩阵变化操作,从而对绘制的view内容发生动画。

到这里就讲完了view tween动画的执行原理

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/u010019468/article/details/73469410
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞