美文网首页
View绘制流程(三)

View绘制流程(三)

作者: 小川君 | 来源:发表于2018-11-22 18:35 被阅读0次

    在调用requestLayout之后,最终调用到了performTraversals
    《深入理解Android卷III》将performTraversals分为了四个过程:应用程序的预测量过程、WMS布局窗口阶段、最终测量阶段、最终布局控件树阶段、绘制阶段

    // ViewRootImpl#performTraversals
        private void performTraversals() {
            // cache mView since it is used so much below...
            // mView为DecorView对象
            final View host = mView;
    
            if (DBG) {
                System.out.println("======================================");
                System.out.println("performTraversals");
                host.debug();
            }
    
            if (host == null || !mAdded)
                return;
            // 是否正在遍历view
            mIsInTraversal = true;
            // 是否需要绘制
            mWillDrawSoon = true;
            // 界面尺寸是否发生变化
            boolean windowSizeMayChange = false;
            // 是否是新界面
            boolean newSurface = false;
            // 界面是否发生变化(看着好像与上面的变量冲突了)
            boolean surfaceChanged = false;
            // DecorView的layoutParams
            WindowManager.LayoutParams lp = mWindowAttributes;
            // DecorView的宽度与高度
            int desiredWindowWidth;
            int desiredWindowHeight;
    
            final int viewVisibility = getHostVisibility();
            final boolean viewVisibilityChanged = !mFirst
                    && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                    // Also check for possible double visibility update, which will make current
                    // viewVisibility value equal to mViewVisibility and we may miss it.
                    || mAppVisibilityChanged);
            mAppVisibilityChanged = false;
            final boolean viewUserVisibilityChanged = !mFirst &&
                    ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
    
            WindowManager.LayoutParams params = null;
            if (mWindowAttributesChanged) {
                mWindowAttributesChanged = false;
                surfaceChanged = true;
                params = lp;
            }
            CompatibilityInfo compatibilityInfo =
                    mDisplay.getDisplayAdjustments().getCompatibilityInfo();
            if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
                params = lp;
                mFullRedrawNeeded = true;
                mLayoutRequested = true;
                if (mLastInCompatMode) {
                    params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                    mLastInCompatMode = false;
                } else {
                    params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                    mLastInCompatMode = true;
                }
            }
    
            mWindowAttributesChangesFlag = 0;
            // mWinFrame用来Activity窗口的宽度和高度
            Rect frame = mWinFrame;
            // 如果是第一次执行界面的测量绘制工作的话
            if (mFirst) {
                // 是否全部绘制
                mFullRedrawNeeded = true;
                // 是否重新布局view的位置即layout
                mLayoutRequested = true;
    
                final Configuration config = mContext.getResources().getConfiguration();
                //  判断界面是否有状态栏和输入键盘
                if (shouldUseDisplaySize(lp)) {
                    // NOTE -- system code, won't try to do compat mode.
                    // 如果是的话,那么就设置当前activity的宽度和高度为减去状态栏和键盘弹的
                    Point size = new Point();
                    mDisplay.getRealSize(size);
                    desiredWindowWidth = size.x;
                    desiredWindowHeight = size.y;
                } else {
                    // 如果没有的话,就是整个屏幕的高度
                    desiredWindowWidth = dipToPx(config.screenWidthDp);
                    desiredWindowHeight = dipToPx(config.screenHeightDp);
                }
                    // ....代码省略
            } else {
                  // 如果不是第一次执行测量工作的话,就会从上一次执行完并保存到mWinFrame/frame中的宽高
                  // 度中进行获取
                desiredWindowWidth = frame.width();
                desiredWindowHeight = frame.height();
                  // mWidth也是上一次执行完测量并进行保存的值,但是与frame的不同之处下面再说
                if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                    if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                    // 如果两值不同 则说明界面发生了变化,设置界面发生变化标识,
                    mFullRedrawNeeded = true;
                    mLayoutRequested = true;
                    windowSizeMayChange = true;
                }
            }
    
            // .... 代码省略
    
            // Execute enqueued actions on every traversal in case a detached view enqueued an action
            // 这里执行RunQueue中的回调。当我们在子线程中是,可以通过View.post()切换到主线程
            // 为什么可以切换到主线程呢,就是因为RunQueue,通过post将Runnable添加到RunQueue队列中。
            // 然后RunQueue将Runnable发送到主线程的Handler。
            getRunQueue().executeActions(mAttachInfo.mHandler);
    
            boolean insetsChanged = false;
            //  是否需要重新重新layout布局,mLayoutRequested在第一次进行绘制为true,非第一次
            // 但是界面尺寸发生变化时也会为true,所以简单推测就是当前界面需不需要重新绘制。
            boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
            if (layoutRequested) {
                // 这里就是预测量阶段,遍历整个view树,得到顶层DecorView的尺寸,主要是通过measureHierarchy()
                final Resources res = mView.getContext().getResources();
                // 如果界面需要重新绘制 并且是第一次绘制的话
                if (mFirst) {
                    // make sure touch mode code executes by setting cached value
                    // to opposite of the added touch mode.
                    // 设置触摸模式
                    mAttachInfo.mInTouchMode = !mAddedTouchMode;
                    ensureTouchModeLocally(mAddedTouchMode);
                } else {
                    if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
                        insetsChanged = true;
                    }
                    if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                        insetsChanged = true;
                    }
                    if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                        insetsChanged = true;
                    }
                    if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                        mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                        if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
                                + mAttachInfo.mVisibleInsets);
                    }
                    if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
                        insetsChanged = true;
                    }
                    if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                        insetsChanged = true;
                    }
                    // 在非第一次绘制窗口 并且窗口的宽高是wrap_content时,则需要重新为窗口设置尺寸
                    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                            || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                        windowSizeMayChange = true;
    
                        if (shouldUseDisplaySize(lp)) {
                            // NOTE -- system code, won't try to do compat mode.
                            Point size = new Point();
                            mDisplay.getRealSize(size);
                            desiredWindowWidth = size.x;
                            desiredWindowHeight = size.y;
                        } else {
                            Configuration config = res.getConfiguration();
                            desiredWindowWidth = dipToPx(config.screenWidthDp);
                            desiredWindowHeight = dipToPx(config.screenHeightDp);
                        }
                    }
                }
                // 因为上面已经做过判断了 如果是第一次执行刷新,那么一定会去计算屏幕的尺寸,所以只有在
                // 非第一次的时候会做测量,如果屏幕的layoutparams是MATCH_PARENT类型的,说明尺寸是
                // 确定的,(其实WARP_CONTENT和MATCH_PARENT对于顶层view是一样的),而如果是WRAP_CONTENT类型的话,
                // 网上说的是悬浮窗类型的,因为如果一个activity的顶层view是WRAP_CONTENT,也可以理解为
                // 非全屏的,那么此Activity的会覆盖到上一个Activity上,并且上个Activity的界面会显示一部分出来
                // 所以下面的测量也是简单的将顶层view的尺寸设置为全屏,具体多大,会在下面再根据子view测量
                // Ask host how big it wants to be
                // 无论第一次还是非第一次,但凡窗口发生了变化都会通过measureHierarchy对view树进行预测量,
                // 这里的目的主要是测量DecorView的尺寸
                windowSizeMayChange |= measureHierarchy(host, lp, res,
                        desiredWindowWidth, desiredWindowHeight);
            }
            // ...代码省略
            if (layoutRequested) {
                // Clear this now, so that if anything requests a layout in the
                // rest of this function we will catch it and re-run a full
                // layout pass.
                mLayoutRequested = false;
            }
            // 这里是判断窗口是否需要重新测量,
            boolean windowShouldResize = layoutRequested && windowSizeMayChange
                && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
                    || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                            frame.width() < desiredWindowWidth && frame.width() != mWidth)
                    || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                            frame.height() < desiredWindowHeight && frame.height() != mHeight));
            windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
    
            // If the activity was just relaunched, it might have unfrozen the task bounds (while
            // relaunching), so we need to force a call into window manager to pick up the latest
            // bounds.
            windowShouldResize |= mActivityRelaunched;
    
            // Determine whether to compute insets.
            // If there are no inset listeners remaining then we may still need to compute
            // insets in case the old insets were non-empty and must be reset.
            final boolean computesInternalInsets =
                    mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
                    || mAttachInfo.mHasNonEmptyGivenInternalInsets;
    
            boolean insetsPending = false;
            int relayoutResult = 0;
            boolean updatedConfiguration = false;
    
            final int surfaceGenerationId = mSurface.getGenerationId();
    
            final boolean isViewVisible = viewVisibility == View.VISIBLE;
            final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
            // 标识一
            if (mFirst || windowShouldResize || insetsChanged ||
                    viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
                mForceNextWindowRelayout = false;
    
                 // ...代码省略  
    
                try {
                   //  .....
                    // 这里就是布局窗口阶段,通过WMS来计算窗口的大小,并最终保存到入参里面
                    // relayoutWindow是用来请求WindowManagerService服务计算Activity窗口大小的
                    // 计算完毕之后,Activity窗口的大小就会保存在ViewRootImpl类的成员变量mWinFrame中
                    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    
                    //  ....
    
                    final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
                            mAttachInfo.mOverscanInsets);
                    contentInsetsChanged = !mPendingContentInsets.equals(
                            mAttachInfo.mContentInsets);
                    final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
                            mAttachInfo.mVisibleInsets);
                    final boolean stableInsetsChanged = !mPendingStableInsets.equals(
                            mAttachInfo.mStableInsets);
                    final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                    final boolean surfaceSizeChanged = (relayoutResult
                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
                    final boolean alwaysConsumeNavBarChanged =
                            mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
    
                 // .... 代码省略
                // 如果Activity没有处于暂停状态
                if (!mStopped || mReportNextDraw) {
                    boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                            (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                    // WMS测量出来的宽高尺寸与上次测量的不一致,则说明界面发生了变化,需要重新绘制
                    if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                            || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                            updatedConfiguration) {
                        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;
                        }
                        // 这里是进行重新测量的的地方
                        if (measureAgain) {
                            if (DEBUG_LAYOUT) Log.v(mTag,
                                    "And hey let's measure once more: width=" + width
                                    + " height=" + height);
                            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                        }
    
                        layoutRequested = true;
                    }
                }
            } else {
                // Not the first pass and no window/insets/visibility change but the window
                // may have moved and we need check that and if so to update the left and right
                // in the attach info. We translate only the window frame since on window move
                // the window manager tells us only for the new frame but the insets are the
                // same and we do not want to translate them more than once.
                // 这里是对应 标识一的if,如果不需要重新绘制,这里会判断窗口是否发生了 移动,
                // 如果移动了则会执行移动动画
                maybeHandleWindowMove(frame);
            }
            // 这里判断是否需要重新布局view树
            final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
            boolean triggerGlobalLayoutListener = didLayout
                    || mAttachInfo.mRecomputeGlobalAttributes;
            if (didLayout) {
                //  布局view树阶段
                // 执行onLayout
                performLayout(lp, mWidth, mHeight);
    
                // By this point all views have been sized and positioned
                // We can compute the transparent area
    
                if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
                    // start out transparent
                    // TODO: AVOID THAT CALL BY CACHING THE RESULT?
                    host.getLocationInWindow(mTmpLocation);
                    mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
                            mTmpLocation[0] + host.mRight - host.mLeft,
                            mTmpLocation[1] + host.mBottom - host.mTop);
    
                    host.gatherTransparentRegion(mTransparentRegion);
                    if (mTranslator != null) {
                        mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
                    }
    
                    if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                        mPreviousTransparentRegion.set(mTransparentRegion);
                        mFullRedrawNeeded = true;
                        // reconfigure window manager
                        try {
                            mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
                        } catch (RemoteException e) {
                        }
                    }
                }
    
                if (DBG) {
                    System.out.println("======================================");
                    System.out.println("performTraversals -- after setFrame");
                    host.debug();
                }
            }
            // 这里判断是否取消绘制
            boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
    
            if (!cancelDraw && !newSurface) {
                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
                        mPendingTransitions.get(i).startChangingAnimations();
                    }
                    mPendingTransitions.clear();
                }
                // 绘制阶段  
                // 如果不取消 回调onDraw
                performDraw();
            } else {
                if (isViewVisible) {
                    // Try again,这里是重新绘制
                    scheduleTraversals();
                } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
                        mPendingTransitions.get(i).endChangingAnimations();
                    }
                    mPendingTransitions.clear();
                }
            }
    
            mIsInTraversal = false;
        }
    
    

    注意的地方就只有一点就是mWidth、mHeight和mWinFrame中的宽高值的不同,前者是应用程序主动向WMS请求绘制,然后绘制的结果保存到mWinFrame,并赋值给mWidth/mHeight,此时mWinFrame中的宽高值是与mWidth/mHeight一样的;而如果由于某些原因,WMS主动绘制计算,然后应用程序被动接收绘制结果并保存到mWinFrame,此时mWinFrame中的宽高度和mWidth/mHeight是不同的
    performTraversals是绘制流程的入口,从测量到布局最后再到绘制

    // FrameLayout#onMeasure
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int count = getChildCount();
            //  首先明确一点widthMeasureSpec/heightMeasureSpec是父view传递过来的
            // 根据父view的宽高规格和子view的宽高规格共同确定的(下面会细说,影响的主要是规格)
            // 而下面则是判断父view的宽高是否是不确定的,也就是宽高是否是wrap_content的
            final boolean measureMatchParentChildren =
                    MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                    MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
            mMatchParentChildren.clear();
            // 本方法的作用就是用来测量当前view的宽高的,并用maxHeight和maxWidth储存宽高,
            // 而此方法是FrameLayout的方法,根据FrameLayout的特征,如果FrameLayout是wrap_content
            // 类型的,那么FrameLayout的宽高则根据子view中最大的view的宽高来设定自己的宽高(可能还有padding和margin)
            int maxHeight = 0;
            int maxWidth = 0;
            int childState = 0;
            //  这里遍历子view
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (mMeasureAllChildren || child.getVisibility() != GONE) {
                    //  这里测量子view的宽高(measureChildWithMargins下面再细说)
                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    //  根据测量的当前view 的宽高与上一次遍历测量的子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());
                    // 如果当前view(FrameLayot)的宽高是AS_MOST类型的,并且当前子view是的宽高是
                    // MATCH_PARENT 类型的,则将当前子view先保存到mMatchParentChildren中。
                    if (measureMatchParentChildren) {
                        if (lp.width == LayoutParams.MATCH_PARENT ||
                                lp.height == LayoutParams.MATCH_PARENT) {
                            mMatchParentChildren.add(child);
                        }
                    }
                }
            }
    
            // Account for padding too
            // 为当前view已经测量出来的宽高再添加背景的padding
            maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
            maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
    
            // Check against our minimum height and width
            // 已经测量出来的宽高与设置的最小宽高对比,并取最大值。如果当前为当前view设置了图片背景,则
            // 与背景的宽高对比,并取最大值。
            maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
            maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
            // 当前view的背景的宽高对比(如果当前背景不为空的话),并取最大值。
            // 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下面在细说)
            setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                    resolveSizeAndState(maxHeight, heightMeasureSpec,
                            childState << MEASURED_HEIGHT_STATE_SHIFT));
            // 上面说,如果当前view的宽高类型是AS_MOST类型的,并且ziview是MATCH_PARENT类型的,会
            // 将此子view保存到mMatchParentChildren
            count = mMatchParentChildren.size();
            if (count > 1) {
                for (int i = 0; i < count; i++) {
                    final View child = mMatchParentChildren.get(i);
                    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
                    final int childWidthMeasureSpec;
                    if (lp.width == LayoutParams.MATCH_PARENT) {
                        // 获取当前view的内容区域宽度(如果ziview是MATCH_PARENT类型的,那么其宽高就是父         
                        // view的内容宽高---对于FrameLayout来说)
                        final int width = Math.max(0, getMeasuredWidth()
                                - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                                - lp.leftMargin - lp.rightMargin);
                        // 为子view重新设置宽度MeasureSpec的规格和尺寸
                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                                width, MeasureSpec.EXACTLY);
                    } else {
                        // getChildMeasureSpec,根据当前view以及当前子view的规格,返回当前子view合适的规格和尺寸。下面会在细说
                        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);
                    }
                    // 这里重新测量ziview的宽高
                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
            }
        }
    
    

    上面的代码我都有写注释,细节就不在说了,onMeasure本身的作用就是测量当前view的宽高,并将值进行保存,而如果当前view是ViewGroup类型的,那么就会先去遍历子view,先去测量子view的宽高,因为有的父view的宽高会收子view的影响,比如wrap_content类型的FrameLayout,然后通过子view的宽高最终会得到一个合适的当前父view的宽高。
    上面有几个方法没有细说,
    measureChildWithMargins测量子view的宽高,涉及view树的遍历;
    resolveSizeAndState根据当前view的规格返回相应的尺寸(子view的状态暂且不考虑);
    getChildMeasureSpec根据当前view以及当前子view的规格,返回当前子view合适的规格和尺寸。

    // measureChildWithMargins
    // child:当前子view的对象;
    // parentWidthMeasureSpec/parentHeightMeasureSpec:当前(父)view的宽高的规格和尺寸;
    // widthUsed/heightUsed::这里为0,好像是权重有关,这里不再细究,按0处理
        protected void measureChildWithMargins(View child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            // 获取到子view的LayoutParams
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 根据父view的规格尺寸、父view的padding和子view的marging、子view的规格尺寸,得到一个合适的规格尺寸
            // getChildMeasureSpec这里也用到了,下面一同说
            final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                    mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                            + widthUsed, lp.width);
            final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                    mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                            + heightUsed, lp.height);
            // 根据得到的宽高的规格属性通过调用child的measure测量子view的宽高,因为measure最终会调用
            // onMeasure,所以最后又回到了onMeasure方法,但是此时的onMeasure是child的,如果child也是
            // 一个ViewGroup类型的,也会开始进行遍历,最终调用的最小的View的onMeasure()
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    
    // View#onMeasure
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
    // View#getDefaultSize
       public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            // 获取到测量出来的当前view的规格尺寸
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
    
            switch (specMode) {
            // 这个case可以忽略
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            // 下面两个感觉好敷衍,最终返回的就是传递过来的尺寸(如果没有重写onMeasure而是使用默认值的话)
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }
    // View#setMeasuredDimension
        protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
            boolean optical = isLayoutModeOptical(this);
            if (optical != isLayoutModeOptical(mParent)) {
                Insets insets = getOpticalInsets();
                int opticalWidth  = insets.left + insets.right;
                int opticalHeight = insets.top  + insets.bottom;
    
                measuredWidth  += optical ? opticalWidth  : -opticalWidth;
                measuredHeight += optical ? opticalHeight : -opticalHeight;
            }
            setMeasuredDimensionRaw(measuredWidth, measuredHeight);
        }
    // View#setMeasuredDimensionRaw
        private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
            // 将最终尺寸赋值给mMeasuredWidth / mMeasuredHeight 
            // 而在上面的onMeasure方法里面遍历子view的时候,通过child.getMeasuredWidth()得到的测量值,
            // 其实就是这两个属性值
            mMeasuredWidth = measuredWidth;
            mMeasuredHeight = measuredHeight;
    
            mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
        }
    
    

    所以measureChildWithMargins会测量完整个View树中的view的宽高尺寸;然后我们来看前面出现两次的方法getChildMeasureSpec,再说onMeasure开头的时候,onMeasure的参数是从父view传递过来,是通过父view以及子view的规格共同处理得到的 ,但是这个方法是ViewGroup独有的

    // ViewGroup#getChildMeasureSpec
    // spec是父view的规格尺寸;
    // padding是父view的padding值+子view的margin值
    // childDimension :子view的宽高尺寸  
     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
            // 首先获取到父view的规格及尺寸
            int specMode = MeasureSpec.getMode(spec);
            int specSize = MeasureSpec.getSize(spec);
            // 父view的尺寸 - 相对间距  = 可以给子view的最大尺寸
            int size = Math.max(0, specSize - padding);
            // 最终尺寸和规格值
            int resultSize = 0;
            int resultMode = 0;
            // 首先判断父view的规格
            switch (specMode) {
            // Parent has imposed an exact size on us
            case MeasureSpec.EXACTLY:
                // 如果父view的规格是确定的,并且子view也指定了一个尺寸,则设置子view的尺寸值为指定的
                // 尺寸值,规格为精确值规格,即 MeasureSpec.EXACTLY    
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size. So be it.
                    // 如果父view的规格是确定的,虽然没有为子view指定特定的值,但是设置了子view尺寸为
                    // MATCH_PARENT,则为子view指定父view最大可以给的值,并设置子view的规格为MeasureSpec.EXACTLY
                    resultSize = size;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    // 如果父view的规格是确定的,不但没有为子view指定特定的值,还设置子view尺寸为
                    // WRAP_CONTENT,则为子view指定父view最大可以给的值,并设置子view的规格为MeasureSpec.AT_MOST
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // Parent has imposed a maximum size on us
            case MeasureSpec.AT_MOST:
                if (childDimension >= 0) {
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    // 如果父view的规格是不确定的,但是子view尺寸为MATCH_PARENT,
                    // 则为子view指定父view最大可以给的值,并设置子view的规格为MeasureSpec.EXACTLY
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    // 如果父view的规格是不确定的,并且子view尺寸为WRAP_CONTENT,
                    // 则为子view指定父view最大可以给的值,并设置子view的规格为MeasureSpec.AT_MOST
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // Parent asked to see how big we want to be
            // 这个不考虑
            case MeasureSpec.UNSPECIFIED:
                if (childDimension >= 0) {
                    // Child wants a specific size... let him have it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size... find out how big it should
                    // be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            }
            //noinspection ResourceType
            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
        }
    

    所以上面的方法是根据父view以及子view的规格,最终得出了子view的规格以及尺寸,小结一下就是:

    1、如果为子view指定了一个精确值,则不管父view的规格,直接设置子view的尺寸为指定的值,规格为EXACTLY
    2、
    3、如果父view的规格不确定,
    2、不管父view的规格到底确不确定,子view的尺寸始终是父view可以给子view的最大尺寸值
    3、如果父view的规格是确定的,即EXACTLY,那么规格则分两种情况(父view规格确定,那么只需要判
    断子view的规格即可):
    1)如果子view的规格确定,即MATCH_PARENT,则设置MeasureSpec.EXACTLY
    2)如果子view的规格不确定,即WRAP_CONTENT,则设置MeasureSpec.AT_MOST
    4、如果父view的规格是不确定,那就不用再分情况了,子view的规格都是AT_MOST  ,主要是因为父view的
    尺寸不确定,子view也没办法去掉具体的尺寸值,所以先按照AT_MOST来设置。
    (还记不记得在onMeasure中会将父view是AT_MOST情况下的子view是MATCH_PARENT的情况,都会保
    存到一个集合中,等到整个view树测量完毕并且为父view设置了宽高尺寸之后,会在重新测量这些子view,
    就是这个原因)
    

    然后我们看resolveSizeAndState

    // View#resolveSizeAndState
    // size:预测量的当前view的最终尺寸
    // measureSpec:当前view宽高的的规格尺寸
        public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
            final int specMode = MeasureSpec.getMode(measureSpec);
            final int specSize = MeasureSpec.getSize(measureSpec);
            final int result;
            switch (specMode) {
                // 如果当前view的规格是AT_MOST即没有设置精确值的话
                case MeasureSpec.AT_MOST:
                    // 其实两个值都可以用,相对来说使用比较小的值,为了在绘制时少更快一些 
                    if (specSize < size) {
                        result = specSize | MEASURED_STATE_TOO_SMALL;
                    } else {
                        result = size;
                    }
                    break;
                case MeasureSpec.EXACTLY:
                    // 如果是确定值的话,则使用specSize
                    result = specSize;
                    break;
                case MeasureSpec.UNSPECIFIED:
                default:
                    result = size;
            }
            return result | (childMeasuredState & MEASURED_STATE_MASK);
        }
    

    resolveSizeAndState看完,感觉有一种前功尽弃感,为什么,前面写了好多的代码都是为了计算确定当前view的宽高,而这里简简单单的一句判断,可能就会不再需要前面计算出的宽高了,当然这也是没有办法,都是为了防止AT_MOST这种情况的出现。

    我们来看布局的方法

    ViewRootImpl#performLayout
     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                int desiredWindowHeight) {
            mLayoutRequested = false;
            mScrollMayChange = true;
            mInLayout = true;
    
            final View host = mView;
            if (host == null) {
                return;
            }
            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
                Log.v(mTag, "Laying out " + host + " to (" +
                        host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
            try {
                // 调用DecorView的layout方法,其中左侧相对于父view左侧的距离为0,顶部
                // 相对于父view顶部的距离为0
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
                mInLayout = false;
                int numViewsRequestingLayout = mLayoutRequesters.size();
                if (numViewsRequestingLayout > 0) {
                    // requestLayout() was called during layout.
                    // If no layout-request flags are set on the requesting views, there is no problem.
                    // If some requests are still pending, then we need to clear those flags and do
                    // a full request/measure/layout pass to handle this situation.
                    ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                            false);
                    if (validLayoutRequesters != null) {
                        // Set this flag to indicate that any further requests are happening during
                        // the second pass, which may result in posting those requests to the next
                        // frame instead
                        mHandlingLayoutInLayoutRequest = true;
    
                        // Process fresh layout requests, then measure and layout
                        int numValidRequests = validLayoutRequesters.size();
                        for (int i = 0; i < numValidRequests; ++i) {
                            final View view = validLayoutRequesters.get(i);
                            Log.w("View", "requestLayout() improperly called by " + view +
                                    " during layout: running second layout pass");
                            view.requestLayout();
                        }
                        measureHierarchy(host, lp, mView.getContext().getResources(),
                                desiredWindowWidth, desiredWindowHeight);
                        mInLayout = true;
                        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
                        mHandlingLayoutInLayoutRequest = false;
    
                        // Check the valid requests again, this time without checking/clearing the
                        // layout flags, since requests happening during the second pass get noop'd
                        validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                        if (validLayoutRequesters != null) {
                            final ArrayList<View> finalRequesters = validLayoutRequesters;
                            // Post second-pass requests to the next frame
                            getRunQueue().post(new Runnable() {
                                @Override
                                public void run() {
                                    int numValidRequests = finalRequesters.size();
                                    for (int i = 0; i < numValidRequests; ++i) {
                                        final View view = finalRequesters.get(i);
                                        Log.w("View", "requestLayout() improperly called by " + view +
                                                " during second layout pass: posting in next frame");
                                        view.requestLayout();
                                    }
                                }
                            });
                        }
                    }
    
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            mInLayout = false;
        }
    
    

    我们接下来看layout布局
    ViewRootImpl#performLayout -> ViewGroup#layout -> view#layout ->

      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;
            }
            // 将原先的view的坐标进行保存
            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);
    
            if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
                // 如果发生了变化  则重新布局
                onLayout(changed, l, t, r, b);
              // .. 代码省略 
            }
    
            mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
            mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    
            if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
                mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
                notifyEnterOrExitForAutoFillIfNeeded(true);
            }
        }
    

    这里调用到了onLayout,onLayout的作用是为当前视图的子视图分配位置,所以,在view中的onLayout是一个空实现,而只有在ViewGroup类型的父视图中才会重写。关于setOpticalFrame和setOpticalFrame我们下面会再说
    我们以DecorView为例,DecorView继承于FrameLayout

    // 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();
            // 因为我们是要为子view分配位置,但是位置是相对于父view的,所以要先拿到父view(当前view)的
            // 位置信息
            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;
    
                    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;
    
                    switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                            // 根据子view的居中、靠左、靠右等为子view分配合适的位置
                        case Gravity.CENTER_HORIZONTAL:
                            // 如果是水平居中,
                            // 子view的左边位置 = 父view的左边距 +(父view的宽带 - 子view的宽度)/ 2  + 子view的左margin  - 子view的margin(这里可以看出 margin和居中是会叠加的)
                            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;
                    }
    
                    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);
                }
            }
        }
    
    

    上面我们知道,在onLayout里面会遍历子view并调用子view的layout(),如果子view是viewGroup类型的, 最终也是会走到这里,然后在继续向下遍历,直到找到最底层的view,最后又回到了View#layout();
    在View#layout()中我们有两个方法还没有说到,setOpticalFrame()最终调用的也是setFrame(),因此我们只看setFrame()。

      protected boolean setFrame(int left, int top, int right, int bottom) {
            boolean changed = false;
            // 判断新位置与旧位置不是完全相同,则认为是发生了变化,需要重新刷新界面
            if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
                changed = true;
    
                // Remember our drawn bit
                int drawn = mPrivateFlags & PFLAG_DRAWN;
    
                int oldWidth = mRight - mLeft;
                int oldHeight = mBottom - mTop;
                int newWidth = right - left;
                int newHeight = bottom - top;
                boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
    
                // Invalidate our old position
                invalidate(sizeChanged);
                // 将传递进来的新位置信息进行存储
                mLeft = left;
                mTop = top;
                mRight = right;
                mBottom = bottom;
                mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
    
                mPrivateFlags |= PFLAG_HAS_BOUNDS;
    
                // 如果布局发生变化,会回调onSizeChanged()
                if (sizeChanged) {
                    sizeChange(newWidth, newHeight, oldWidth, oldHeight);
                }
                // ....
            }
            return changed;
        }
    
    

    我们来看invalidata()

        public 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) {
    
            if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                    || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                    || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                    || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
         
                // 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中,并调用父view的invalidateChild
                    damage.set(l, t, r, b);
                    p.invalidateChild(this, damage);
                }
            }
        }
    
    // ViewGourp#invalidataChild
        @Override
        public final void invalidateChild(View child, final Rect dirty) {
            final AttachInfo attachInfo = mAttachInfo;
            ViewParent parent = this;
            if (attachInfo != null) {
              //  .... 代码省略
                do {
                    View view = null;
                    if (parent instanceof View) {
                        view = (View) parent;
                    }
                    parent = parent.invalidateChildInParent(location, dirty);
                    if (view != null) {
                        // Account for transform on current parent
                        Matrix m = view.getMatrix();
                        if (!m.isIdentity()) {
                            RectF boundingRect = attachInfo.mTmpTransformRect;
                            boundingRect.set(dirty);
                            m.mapRect(boundingRect);
                            dirty.set((int) Math.floor(boundingRect.left),
                                    (int) Math.floor(boundingRect.top),
                                    (int) Math.ceil(boundingRect.right),
                                    (int) Math.ceil(boundingRect.bottom));
                        }
                    }
                } while (parent != null);
            }
        }
    
    

    上面是一个do-while循环,直到找到最顶层的view,也就是DecorView,在上一章说过,DecorView的父view是ViewRootImpl,

    // ViewGroup#invalidateChildInParent
        @Override
        public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
            if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
                // either DRAWN, or DRAWING_CACHE_VALID
                if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
                        != FLAG_OPTIMIZE_INVALIDATE) {
                    dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
                            location[CHILD_TOP_INDEX] - mScrollY);
                    if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
                        dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                    }
    
                    final int left = mLeft;
                    final int top = mTop;
    
                    if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                        if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
                            dirty.setEmpty();
                        }
                    }
    
                    location[CHILD_LEFT_INDEX] = left;
                    location[CHILD_TOP_INDEX] = top;
                } else {
    
                    if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                        dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
                    } else {
                        // in case the dirty rect extends outside the bounds of this container
                        dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                    }
                    location[CHILD_LEFT_INDEX] = mLeft;
                    location[CHILD_TOP_INDEX] = mTop;
    
                    mPrivateFlags &= ~PFLAG_DRAWN;
                }
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
                if (mLayerType != LAYER_TYPE_NONE) {
                    mPrivateFlags |= PFLAG_INVALIDATED;
                }
    
                return mParent;
            }
    
            return null;
        }
    
    // ViewRootImpl#invalidateChildInParent
       @Override
        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
            checkThread();
            if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
    
            if (dirty == null) {
                invalidate();
                return null;
            } else if (dirty.isEmpty() && !mIsAnimating) {
                return null;
            }
    
            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;
        }
    

    最后我们来看绘制

    // ViewRootImpl#performDraw:
        private void performDraw() {
    
    // ... 代码省略
    
            try {
                draw(fullRedrawNeeded);
            } finally {
                mIsDrawing = false;
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    // ViewRootImpl#draw()
      private void draw(boolean fullRedrawNeeded) {
            Surface surface = mSurface;
    
            final float appScale = mAttachInfo.mApplicationScale;
            final boolean scalingRequired = mAttachInfo.mScalingRequired;
    
            int resizeAlpha = 0;
            // 获取到我们的绘制区域
            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;
            }
            // 这里的判断是是否需要绘制整个屏幕,
            if (fullRedrawNeeded) {
                mAttachInfo.mIgnoreDirtyState = true;
                dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
            }
    
            // .... 代码省略
    
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                        return;
                    }
        }
    
    // ViewRootImpl#drawSoftware
        private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                boolean scalingRequired, Rect dirty) {
    
                // .... 代码省略 
      
                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;
                    }
                }
            return true;
        }
    
    
        @CallSuper
        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)
             */
    
            // Step 1, draw the background, if needed
            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) {
                // Step 3, draw the content
                // 通过onDraw绘制主体
                if (!dirtyOpaque) onDraw(canvas);
      
                // Step 4, draw the children   绘制子view
                dispatchDraw(canvas);
    
                drawAutofilledHighlight(canvas);
    
                // 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);
    
                // Step 7, draw the default focus highlight
                drawDefaultFocusHighlight(canvas);
    
                if (debugDraw()) {
                    debugDrawFocus(canvas);
                }
    
                // we're done...
                return;
            }
    
            // ...代码省略
    
        }
    

    主要是两个地方,一个是绘制主体,一个是绘制子view
    onDraw在view里面并没有实现,具体的逻辑是根据view的派生类的自身情况具体实现的,比如TextView,这里我们不再多说;dispatchDraw只有ViewGroup的派生类中才会实现

     @Override
      protected void dispatchDraw(Canvas canvas) {
          boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
          final int childrenCount = mChildrenCount;
          final View[] children = mChildren;
          int flags = mGroupFlags;
    
          // .... 代码省略
    
          int clipSaveCount = 0;
          final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
          if (clipToPadding) {
              // 这里去掉padding的区域,只绘制内容区域
              clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
              canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                      mScrollX + mRight - mLeft - mPaddingRight,
                      mScrollY + mBottom - mTop - mPaddingBottom);
          }
          // 遍历子view
          for (int i = 0; i < childrenCount; i++) {
              while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                  final View transientChild = mTransientViews.get(transientIndex);
                  if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                          transientChild.getAnimation() != null) {
                      // 绘制子view
                      more |= drawChild(canvas, transientChild, drawingTime);
                  }
                  transientIndex++;
                  if (transientIndex >= transientCount) {
                      transientIndex = -1;
                  }
              }
    
          if (clipToPadding) {
              canvas.restoreToCount(clipSaveCount);
          }
    
          // .... 代码省略
    
      }
    
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
          return child.draw(canvas, this, drawingTime);
      }
    
      boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
          final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
          // 开启硬件加速
          boolean drawingWithRenderNode = mAttachInfo != null
                  && mAttachInfo.mHardwareAccelerated
                  && hardwareAcceleratedCanvas;
          
          int sx = 0;
          int sy = 0;
          if (!drawingWithRenderNode) {
              //  配合Scroller可以对滚动进行计算
              computeScroll();
              sx = mScrollX;
              sy = mScrollY;
          }
    
           // ...代码省略 
    
          if (!drawingWithDrawingCache) {
              if (drawingWithRenderNode) {
                  mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                  ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
              } else {
                  // Fast path for layouts with no backgrounds
                  if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                      mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                      dispatchDraw(canvas);
                  } else {
                      // 绘制自身
                      draw(canvas);
                  }
              }
          } else if (cache != null) {
    
          }
    
          return more;
      }
    
    

    绘制到这里就结束,小结一下
    调用requestLayout之后,然后调用到了ViewRootImpl的performTraversals方法,在这个方法中,先是通过PerformMeasure对整个view树进行了一次预测量,方便对比窗口是否发生了变化,需要重新测量绘制等操作,然后通过WMS对窗口进行测量,并返回测量结果,拿到测量结果之后就开始进行一系列的测量、布局、绘制的工作

    相关文章

      网友评论

          本文标题:View绘制流程(三)

          本文链接:https://www.haomeiwen.com/subject/wjjafqtx.html