欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Animation动画原理源码分析

程序员文章站 2024-03-24 20:59:28
...

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动画的执行原理