View机制解析

  源码解析

Posted by MrCodeSniper on April 1, 2018

我们之前分析Window机制时已经清楚的理解了我们设置的View 就是DecorView的mContentParent下的View树

起点

WindowManagerGlobal

DecorView通过 WindowManager添加view到window显示与用户交互

ViewRootImpl 是用来连接 WindowManager 和 DecorView 的桥梁

在 WindowManagerGlobal 的 addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) 方法中,创建了 ViewRootImpl 对象,将 ViewRootImpl 和 DecorView 相关联 root.setView(view, wparams, panelParentView);

Android中的任何一个布局、任何一个控件其实都是直接或间接继承自View实现的 这些View应该都具有相同的绘制流程与机制才能显示到屏幕上 每一个View的绘制过程都必须经历三个最主要的过程,也就是measure、layout和draw。

而整个view树的绘制就是通过ViewRootImpl将decorview绘制完成交由WindowManager在window上显示

ViewRootImpl

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
       synchronized (this) {
           if (mView == null) {
			// 将 decorView 设置给全局的 mView
               mView = view;
			...
			// 标记已经添加了 decorView
			mAdded = true;
			...
			// 第一次发起布局,在添加到 WindowManager 之前
			// 确保在接收其他系统事件之前完成重新布局
			requestLayout();
			...
			// 利用 mWindowSession 以跨进程的方式向 WMS 发起一个调用,从而将DecorView 最终添加到 Window 上
		  	try {
		  	    mOrigWindowType = mWindowAttributes.type;
		  	    mAttachInfo.mRecomputeGlobalAttributes = true;
		  	    collectViewAttributes();
		  	    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(),        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);
               } 
			...
		}
	}
}

这个源码方法的重点在与requestLayout(); 它是绘制步骤的关键我们继续深入

 @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

最终调用scheduleTraversals();

 void scheduleTraversals() {
       if (!mTraversalScheduled) {
           mTraversalScheduled = true;
           mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
		// 发送消息,调用 mTraversalRunnable
           mChoreographer.postCallback(
                   Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           if (!mUnbufferedInputDispatch) {
               scheduleConsumeBatchedInput();
           }
           notifyRendererOfFramePending();
           pokeDrawLockIfNeeded();
       }
   }

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

   final class TraversalRunnable implements Runnable {
       @Override
       public void run() {
		// 内部调用了 performTraversals()
           doTraversal();
       }
   }

在主线程调用doTraversal(); 最后在深入还是要看performTraversals()

private void performTraversals() {

	// 计算 Activity 中 window 的宽高等等
	...

	if (!mStopped || mReportNextDraw) {
               boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                       (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
               if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                       || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                       updatedConfiguration) {
                   // 得到 view 宽高的规格
                   // mWidth 和 mHeight 即用来描述 Activity 窗口宽度和高度
                   // lp.width 和 lp.height 就是 DecorView 的宽高
                   int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                   int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                   if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                           + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                           + " mHeight=" + mHeight
                           + " measuredHeight=" + host.getMeasuredHeight()
                           + " coveredInsetsChanged=" + contentInsetsChanged);

                    // Ask host how big it wants to be
                    // 开始执行测量工作,测量是从这里发起的
                   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                   // Implementation of weights from WindowManager.LayoutParams
                   // We just grow the dimensions as needed and re-measure if
                   // needs be
                   int width = host.getMeasuredWidth();
                   int height = host.getMeasuredHeight();
                   boolean measureAgain = false;

                   // 检查是否需要重新测量
                   if (lp.horizontalWeight > 0.0f) {
                       width += (int) ((mWidth - width) * lp.horizontalWeight);
                       childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                               MeasureSpec.EXACTLY);
                       measureAgain = true;
                   }
                   if (lp.verticalWeight > 0.0f) {
                       height += (int) ((mHeight - height) * lp.verticalWeight);
                       childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                               MeasureSpec.EXACTLY);
                       measureAgain = true;
                   }

                   // 需要再次测量的话,就再执行一遍 performMeasure
                   if (measureAgain) {
                       if (DEBUG_LAYOUT) Log.v(mTag,
                               "And hey let's measure once more: width=" + width
                               + " height=" + height);
                       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                   }

                   layoutRequested = true;
               }
           }

	...

       final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
       boolean triggerGlobalLayoutListener = didLayout
               || mAttachInfo.mRecomputeGlobalAttributes;
       if (didLayout) {
           // 执行布局工作,布局是从这里发起的
           performLayout(lp, mWidth, mHeight);

	...

	if (!cancelDraw && !newSurface) {
           if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
               for (int i = 0; i < mPendingTransitions.size(); ++i) {
                   mPendingTransitions.get(i).startChangingAnimations();
               }
               mPendingTransitions.clear();
           }
           // 执行绘制工作,绘制是从这里发起的
           performDraw();
       } 

	...

}

performTraversals() 方法的代码很长很长,但是我们关注点就可以放在三大流程上。其他的代码因为自己能力欠缺,并不能一一说出这些代码的作用。所以我们接下来就把重点放在:

1.getRootMeasureSpec

2.performMeasure

3.performLayout

4.performDraw

getRootMeasureSpec

windowSizeMayChange |= measureHierarchy(host, lp, res,
        desiredWindowWidth, desiredWindowHeight);

在 measureHierarchy 方法中已经调用了 performMeasure 来进行测量 这里只是为了确定 window 的大小而做的测量辅助

在 measureHierarchy 中,确定了 DecorView 的 MeasureSpec 。其中 childWidthMeasureSpec 和 childHeightMeasureSpec 即为 DecorView 对应的 MeasureSpec 。

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {

    case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        // Window can resize. Set max size for root view.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        // Window wants to be an exact size. Force root view to be that size.
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}

如果是 MATCH_PARENT ,那么对应的就是窗口大小;

如果是 WRAP_CONTENT ,那么不能超过窗口大小;

固定大小,那么就是大小就是传入的 lp.width/lp.height 了。

ViewGroup中 计算子 View 测量规格的 getChildMeasureSpec 方法

也是根据父容器的规格确定子容器

具体可用这张图表示

总结:对于 DecorView 来说,其 MeasureSpec 是由窗口的大小和自身的 LayoutParams 来共同决定的;而对于普通的 View 来说,其 MeasureSpec 是由父容器的 MeasureSpec 和自身的 LayoutParams 共同决定的。

performMeasure

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        // 进行测量
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

在 performMeasure 中调用了 measure 方法。说到底,DecorView 只是一个View所以我们又要进入 View 类中去看下。

View 的 measure 方法内部是调用了 onMeasure

这里小提一下,我们都知道 DecorView 其实是一个 FrameLayout ,所以 onMeasure 应该在 FrameLayout 中去看

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    // 判断当前 framelayout 布局的宽高是否至少一个是 match_parent 或者精确值 ,如果是则置 measureMatchParent 为 false .
    final boolean measureMatchParentChildren =
            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    mMatchParentChildren.clear();

    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;

    // 遍历不为 GONE 的子 view
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            // 对每一个子 View 进行测量
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            // 寻找子 View 中宽高的最大者,因为如果 FrameLayout 是 wrap_content 属性
            // 那么它的宽高取决于子 View 中的宽高最大者
            maxWidth = Math.max(maxWidth,
                    child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
            maxHeight = Math.max(maxHeight,
                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
            childState = combineMeasuredStates(childState, child.getMeasuredState());
            // 如果 FrameLayout 为 wrap_content 且 子 view 的宽或高为 match_parent ,那么就添加到 mMatchParentChildren 中
            if (measureMatchParentChildren) {
                if (lp.width == LayoutParams.MATCH_PARENT ||
                        lp.height == LayoutParams.MATCH_PARENT) {
                    mMatchParentChildren.add(child);
                }
            }
        }
    }

    // Account for padding too
    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

    // Check against our minimum height and width
    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // Check against our foreground's minimum height and width
    final Drawable drawable = getForeground();
    if (drawable != null) {
        maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
        maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }
    //设置测量结果
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));

    // 子View中设置为match_parent的个数
    count = mMatchParentChildren.size();
    // 若 FrameLayout 为 wrap_content 且 count > 1
    if (count > 1) {
        for (int i = 0; i < count; i++) {
            final View child = mMatchParentChildren.get(i);
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

            // 如果子 View 的宽度是 match_parent 属性,那么对 childWidthMeasureSpec 修改:
            // 把 widthMeasureSpec 的宽度修改为:framelayout总宽度 - padding - margin,模式设置为 EXACTLY
            final int childWidthMeasureSpec;
            if (lp.width == LayoutParams.MATCH_PARENT) {
                final int width = Math.max(0, getMeasuredWidth()
                        - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                        - lp.leftMargin - lp.rightMargin);
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        width, MeasureSpec.EXACTLY);
            } else {
                // 否则就按照正常的来就行了
                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                        lp.leftMargin + lp.rightMargin,
                        lp.width);
            }

            // 高度同理
            final int childHeightMeasureSpec;
            if (lp.height == LayoutParams.MATCH_PARENT) {
                final int height = Math.max(0, getMeasuredHeight()
                        - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                        - lp.topMargin - lp.bottomMargin);
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        height, MeasureSpec.EXACTLY);
            } else {
                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                        getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
            }
            //对于这部分的子 View 需要重新进行 measure 过程
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    }
}

其实总的来说重要的就只有遍历 child.measure(childWidthMeasureSpec, childHeightMeasureSpec) 这个方法,这是将父容器的 measure 过程传递到子 View 中。

而子 View 被父容器调用了 measure 后,也会调用属于自己的 onMeasure 方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

在onMeasure源码中设置宽高尺寸 使用了getDefaultSize,我们在看下getDefaultSize的源码

public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        //通过MeasureSpec解析获取mode与size 即前两位和后三十位
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
//父控件对子控件不加任何束缚,子元素可以得到任意想要的大小
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST://父控件为子View指定确切大小
        case MeasureSpec.EXACTLY://父控件为子元素指定最大参考尺寸
            result = specSize;
            break;
        }
        return result;
    }

若是 UNSPECIFIED ,则直接返回的就是 getSuggestedMinimumWidth/getSuggestedMinimumHeight 的值;

若是 AT_MOST/EXACTLY ,直接用的就是 specSize 。

而根据我们之前总结出来的表可知,只要 view 不指定固定大小,那么无论是 AT_MOST 还是 EXACTLY ,都是按照 parentSize 来的。

performLayout

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

基本可知,performLayout 是通过调用 DecorView 的 layout 方法来向下传递布局的。所以我们应该继续追踪 FrameLayout 的 layout 方法,其实就是 ViewGroup 的 layout 方法。

@Override
public final void layout(int l, int t, int r, int b) {
    if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
        if (mTransition != null) {
            mTransition.layoutChange(this);
        }
        // 调用 view 的 layout 方法
        super.layout(l, t, r, b);
    } else {  
        // record the fact that we noop'd it; request layout when transition finishes
        mLayoutCalledWhileSuppressed = true;
    }
}

在 ViewGroup 的 layout 方法中又调用了父类的方法 super.layout(l, t, r, b) 。所以我们又要到 View 类中去看。

@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    // 当前布局的四个顶点
    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    // 计算四个顶点的值,判断布局位置是否改变
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);//setOpticalFrame 的内部也是调用 setFrame 方法的。

    // 如果视图的大小和位置发生变化,会调用onLayout()
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {

        // 空方法
        onLayout(changed, l, t, r, b);

        if (shouldDrawRoundScrollbar()) {
            if(mRoundScrollbarRenderer == null) {
                mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
            }
        } else {
            mRoundScrollbarRenderer = null;
        }

        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

        // 调用布局位置改变监听器
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnLayoutChangeListeners != null) {
            ArrayList<OnLayoutChangeListener> listenersCopy =
                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
            int numListeners = listenersCopy.size();
            for (int i = 0; i < numListeners; ++i) {
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
            }
        }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

1.设置当前布局中的四个顶点;2.调用 setFrame 来设置新的顶点位置;3.调用 onLayout 方法;4.回调布局位置改变监听器;

在setFrame(l, t, r, b)判断大小是否改变 改变了就重绘 并回调相关接口

之后回调onLayout方法 因为decorview为framelayout进入其onLayout方法

@Override
   protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
       layoutChildren(left, top, right, bottom, false /* no force left gravity */);
   }
   
   
   void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
       final int count = getChildCount();

       final int parentLeft = getPaddingLeftWithForeground();
       final int parentRight = right - left - getPaddingRightWithForeground();

       final int parentTop = getPaddingTopWithForeground();
       final int parentBottom = bottom - top - getPaddingBottomWithForeground();

       // 遍历子 view
       for (int i = 0; i < count; i++) {
           final View child = getChildAt(i);
           if (child.getVisibility() != GONE) {
               final LayoutParams lp = (LayoutParams) child.getLayoutParams();

               // 子 view 的宽高
               final int width = child.getMeasuredWidth();
               final int height = child.getMeasuredHeight();

               int childLeft;
               int childTop;

               // 得到子 view 的 gravity
               int gravity = lp.gravity;
               if (gravity == -1) {
                   gravity = DEFAULT_CHILD_GRAVITY;
               }

               final int layoutDirection = getLayoutDirection();
               final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
               final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

               // 根据不同的 gravity 来计算 childLeft
               switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                   case Gravity.CENTER_HORIZONTAL:
                       childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                       lp.leftMargin - lp.rightMargin;
                       break;
                   case Gravity.RIGHT:
                       if (!forceLeftGravity) {
                           childLeft = parentRight - width - lp.rightMargin;
                           break;
                       }
                   case Gravity.LEFT:
                   default:
                       childLeft = parentLeft + lp.leftMargin;
               }


               // 根据不同的 gravity 来计算 childTop
               switch (verticalGravity) {
                   case Gravity.TOP:
                       childTop = parentTop + lp.topMargin;
                       break;
                   case Gravity.CENTER_VERTICAL:
                       childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                       lp.topMargin - lp.bottomMargin;
                       break;
                   case Gravity.BOTTOM:
                       childTop = parentBottom - height - lp.bottomMargin;
                       break;
                   default:
                       childTop = parentTop + lp.topMargin;
               }
               // 调用子 view 的 layout 方法
               child.layout(childLeft, childTop, childLeft + width, childTop + height);
           }
       }
   }

在 layoutChildren 中,遍历所有可见的子 View ,然后得到它们的宽高。

再根据不同的 gravity 来计算 childLeft 和 childTop ,最后调用 child.layout 来向子 View 传递下去。

子view根据四个顶点摆放在对应位置

performDraw

private void performDraw() {
    if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
        return;
    }

    final boolean fullRedrawNeeded = mFullRedrawNeeded;
    mFullRedrawNeeded = false;

    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
    try {
        // 调用 draw 方法,fullRedrawNeeded 为是否重新绘制全部视图
        draw(fullRedrawNeeded);
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    ...
}

private void draw(boolean fullRedrawNeeded) {
    
    ...

    // dirty 表示需要绘制的区域
    final Rect dirty = mDirty;
    if (mSurfaceHolder != null) {
        // The app owns the surface, we won't draw.
        dirty.setEmpty();
        if (animating && mScroller != null) {
            mScroller.abortAnimation();
        }
        return;
    }

    // 如果需要全部绘制,那么 dirty 就是整个屏幕了
    if (fullRedrawNeeded) {
        mAttachInfo.mIgnoreDirtyState = true;
        dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
    }

    ...

    // 调用 drawSoftware ,把绘制区域 dirty 传入
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
        return;
    }
        
    ...

}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {

    // Draw with software renderer.
    final Canvas canvas;
    try {
        final int left = dirty.left;
        final int top = dirty.top;
        final int right = dirty.right;
        final int bottom = dirty.bottom;

        //锁定画布,由 dirty 区域决定
        canvas = mSurface.lockCanvas(dirty);

        // The dirty rectangle can be modified by Surface.lockCanvas()
        //noinspection ConstantConditions
        if (left != dirty.left || top != dirty.top || right != dirty.right
                || bottom != dirty.bottom) {
            attachInfo.mIgnoreDirtyState = true;
        }

        // TODO: Do this in native
        canvas.setDensity(mDensity);
    } catch (Surface.OutOfResourcesException e) {
        handleOutOfResourcesException(e);
        return false;
    } catch (IllegalArgumentException e) {
        Log.e(mTag, "Could not lock surface", e);
        // Don't assume this is due to out of memory, it could be
        // something else, and if it is something else then we could
        // kill stuff (or ourself) for no reason.
        mLayoutRequested = true;    // ask wm for a new surface next time.
        return false;
    }

    try {
        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                    + canvas.getWidth() + ", h=" + canvas.getHeight());
            //canvas.drawARGB(255, 255, 0, 0);
        }

        // If this bitmap's format includes an alpha channel, we
        // need to clear it before drawing so that the child will
        // properly re-composite its drawing on a transparent
        // background. This automatically respects the clip/dirty region
        // or
        // If we are applying an offset, we need to clear the area
        // where the offset doesn't appear to avoid having garbage
        // left in the blank areas.
        if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }

        dirty.setEmpty();
        mIsAnimating = false;
        mView.mPrivateFlags |= View.PFLAG_DRAWN;

        if (DEBUG_DRAW) {
            Context cxt = mView.getContext();
            Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
                    ", metrics=" + cxt.getResources().getDisplayMetrics() +
                    ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
        }
        try {
            canvas.translate(-xoff, -yoff);
            if (mTranslator != null) {
                mTranslator.translateCanvas(canvas);
            }
            canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
            attachInfo.mSetIgnoreDirtyState = false;

            // 调用 View 的 draw 方法
            mView.draw(canvas);

            drawAccessibilityFocusedDrawableIfNeeded(canvas);
        } finally {
            if (!attachInfo.mSetIgnoreDirtyState) {
                // Only clear the flag if it was not set during the mView.draw() call
                attachInfo.mIgnoreDirtyState = false;
            }
        }
    } finally {
        try {
            surface.unlockCanvasAndPost(canvas);
        } catch (IllegalArgumentException e) {
            Log.e(mTag, "Could not unlock surface", e);
            mLayoutRequested = true;    // ask wm for a new surface next time.
            //noinspection ReturnInsideFinallyBlock
            return false;
        }

        if (LOCAL_LOGV) {
            Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
        }
    }
    return true;
}

最后调用了mView.draw(canvas);

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)
     */

    // 第一步,画背景
    int saveCount;

    if (!dirtyOpaque) {
        drawBackground(canvas);
    }

    // skip step 2 & 5 if possible (common case)
    // 可能的话,跳过第二步和第五步
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
        // 第三步,画自己的内容
        if (!dirtyOpaque) onDraw(canvas);

        // 第四步,画自己子 view 的内容
        dispatchDraw(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // 第六步,绘制View的装饰,比如 scrollbar 等 (foreground, scrollbars)
        onDrawForeground(canvas);

        // 做完了,直接返回 we're done...
        return;
    }

    /*
     * Here we do the full fledged routine...
     * (this is an uncommon case where speed matters less,
     * this is why we repeat some of the tests that have been
     * done above)
     */

    boolean drawTop = false;
    boolean drawBottom = false;
    boolean drawLeft = false;
    boolean drawRight = false;

    float topFadeStrength = 0.0f;
    float bottomFadeStrength = 0.0f;
    float leftFadeStrength = 0.0f;
    float rightFadeStrength = 0.0f;

    // 第二步,保存 canvas 图层
    int paddingLeft = mPaddingLeft;

    final boolean offsetRequired = isPaddingOffsetRequired();
    if (offsetRequired) {
        paddingLeft += getLeftPaddingOffset();
    }

    int left = mScrollX + paddingLeft;
    int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
    int top = mScrollY + getFadeTop(offsetRequired);
    int bottom = top + getFadeHeight(offsetRequired);

    if (offsetRequired) {
        right += getRightPaddingOffset();
        bottom += getBottomPaddingOffset();
    }

    final ScrollabilityCache scrollabilityCache = mScrollCache;
    final float fadeHeight = scrollabilityCache.fadingEdgeLength;
    int length = (int) fadeHeight;

    // clip the fade length if top and bottom fades overlap
    // overlapping fades produce odd-looking artifacts
    if (verticalEdges && (top + length > bottom - length)) {
        length = (bottom - top) / 2;
    }

    // also clip horizontal fades if necessary
    if (horizontalEdges && (left + length > right - length)) {
        length = (right - left) / 2;
    }

    if (verticalEdges) {
        topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
        drawTop = topFadeStrength * fadeHeight > 1.0f;
        bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
        drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
    }

    if (horizontalEdges) {
        leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
        drawLeft = leftFadeStrength * fadeHeight > 1.0f;
        rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
        drawRight = rightFadeStrength * fadeHeight > 1.0f;
    }

    saveCount = canvas.getSaveCount();

    int solidColor = getSolidColor();
    if (solidColor == 0) {
        final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

        if (drawTop) {
            canvas.saveLayer(left, top, right, top + length, null, flags);
        }

        if (drawBottom) {
            canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
        }

        if (drawLeft) {
            canvas.saveLayer(left, top, left + length, bottom, null, flags);
        }

        if (drawRight) {
            canvas.saveLayer(right - length, top, right, bottom, null, flags);
        }
    } else {
        scrollabilityCache.setFadeColor(solidColor);
    }

    // Step 3, draw the content
    if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
    dispatchDraw(canvas);

    // 第五步,绘制边缘效果和恢复图层
    final Paint p = scrollabilityCache.paint;
    final Matrix matrix = scrollabilityCache.matrix;
    final Shader fade = scrollabilityCache.shader;

    if (drawTop) {
        matrix.setScale(1, fadeHeight * topFadeStrength);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, top, right, top + length, p);
    }

    if (drawBottom) {
        matrix.setScale(1, fadeHeight * bottomFadeStrength);
        matrix.postRotate(180);
        matrix.postTranslate(left, bottom);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, bottom - length, right, bottom, p);
    }

    if (drawLeft) {
        matrix.setScale(1, fadeHeight * leftFadeStrength);
        matrix.postRotate(-90);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, top, left + length, bottom, p);
    }

    if (drawRight) {
        matrix.setScale(1, fadeHeight * rightFadeStrength);
        matrix.postRotate(90);
        matrix.postTranslate(right, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(right - length, top, right, bottom, p);
    }

    canvas.restoreToCount(saveCount);

    // Overlay is part of the content and draws beneath Foreground
    if (mOverlay != null && !mOverlay.isEmpty()) {
        mOverlay.getOverlayView().dispatchDraw(canvas);
    }

    // Step 6, draw decorations (foreground, scrollbars)
    onDrawForeground(canvas);
}

draw 过程大概有下面几步:

1.绘制背景:background.draw(canvas) ;

2.保存当前的图层信息(一般来说跳过);

3.绘制自己:onDraw(canvas) ;

4.绘制children:dispatchDraw(canvas) ;

在 dispatchDraw(Canvas canvas) 中,遍历子 View ,然后调用 child.drawChild(Canvas canvas, View child, long drawingTime) 方法来执行子 View 的绘制流程,从而实现了绘制过程的向下传递。

在 draw(Canvas canvas, ViewGroup parent, long drawingTime) 中, 若没有缓存的话,那么调用 draw(canvas) ; 否则直接调用 dispatchDraw(canvas) 分发给子 View 适用于 ViewGroup

5.绘制边缘效果,恢复图层(一般来说跳过);

6.绘制前景装饰:onDrawForeground(canvas) 。

小结

我们把整个过程梳理以下

WindowManagerGlobal.addView()中代码root = new ViewRootImpl(view.getContext(), display)创建了ViewRootImp WindowManagerGlobal.addView()中又调用了ViewRootImp的setView(view, wparams, panelParentView) 与decorview关联

-并在其中调用了requestLayout();

—-其中调用了 scheduleTraversals();

——其中在主线执行doTraversal();

———其中执行performTraversals();

———–其中执行了measureHierarchy根据view的大小调整window大小

———————————接着调用了performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

—————其中执行了DecorView.measure

——————-其中执行了DecorView.onMeasure

———————–其中循环执行了measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

———————– 1.ViewGroup.measure->ViewGroup.onMeasure -> ViewGroup.measureChildWithMargins

———————– 2.View.measure -> View.onMeasure                

———————————接着调用了performLayout(lp, desiredWindowWidth, desiredWindowHeight);

—————其中执行了DecorView.layout 而viewgroup没有layout 进入view的layout

——————-其中执行了DecorView.onLayout 即FrameLayout.onLayout

———————–其中循环执行了child.layout(childLeft, childTop, childLeft + width, childTop + height);根据四个点摆放位置

———————————接着调用了performDraw();

———–接着调用了ViewRootImp的draw(fullRedrawNeeded);

———–接着调用了ViewRootImp的drawSoftware

—————其中执行了DecorView.draw 而viewgroup没有draw 进入view的draw

——————-其中执行了DecorView.onDraw 即FrameLayout.onDraw 但是viewgroup没有这个方法 需要分发给子view执行

——————-接着执行了ViewGroup的dispatchDraw 循环执行drawChild(canvas, transientChild, drawingTime);

——————-其实就是viewgroup下子view执行draw 再执行onDraw()

注:DecorView 其实就是 FrameLayout

End.

Reference

View工作原理