美文网首页
android View 绘制

android View 绘制

作者: 莫库施勒 | 来源:发表于2019-02-20 20:26 被阅读0次

    我们已经知道了事件的传递机制,现在我们来看一下绘制的原理
    我们已经知道View 的绘制是从 ViewRootImpl 的 requestLayout 开始,一直到 performTraversals

        private void performTraversals() {
            ...
            if (layoutRequested) {
               ...
                // Ask host how big it wants to be
                windowSizeMayChange |= measureHierarchy(host, lp, res,
                        desiredWindowWidth, desiredWindowHeight);
            } 
            ...
            if (mFirst || windowShouldResize || insetsChanged ||
                    viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
                  ...
                  if (!mStopped || mReportNextDraw) {
                       ...
                       // Ask host how big it wants to be
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                       ...
                  }
                  ...
             }
              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();
            } 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;
        }
    
        private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
                final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
             ...
           if (!goodMeasure) {
                childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                    windowSizeMayChange = true;
                }
            }
        }
    
        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;
        }
    

    MeaureSpec

    Measure是一个32位的int,高2位代表SpecMode,低30位代表SpecSize。SpecMode表示测量模式,SpecSize指在某种测量模式下规格的大小。其表示如下:

    高2位 含义
    0 UNSPECIFIED
    1 EXACTLY
    2 AT_MOST

    用法

    父类型 子类型 子Spec
    EXACTLY size EXACTLY
    EXACTLY MATCH_PARENT EXACTLY
    EXACTLY WRAP_CONTENT AT_MOST
    AT_MOST size EXACTLY
    AT_MOST MATCH_PARENT AT_MOST
    AT_MOST WRAP_CONTENT AT_MOST

    接下来是performXXX

        private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
             mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    
        private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                int desiredWindowHeight) {
            final View host = mView;
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
            mInLayout = false;
            int numViewsRequestingLayout = mLayoutRequesters.size();
            if (numViewsRequestingLayout > 0) {
                // requestLayout() was called during layout.
                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                        false);
                if (validLayoutRequesters != null) {
                   int numValidRequests = validLayoutRequesters.size();
                   for (int i = 0; i < numValidRequests; ++i) {
                       final View view = validLayoutRequesters.get(i);
                       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,
                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                    if (validLayoutRequesters != null) {
                      final ArrayList<View> finalRequesters = validLayoutRequesters;
                      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);
                                    view.requestLayout();
                               }
                           }
                       });
                    }
                }
            }
        }
        private void performDraw() {
            canvas = mSurface.lockCanvas(dirty);
            try {
               canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
               canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;
    
               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);
                } 
            }
    

    onMeasure

        /**
         * 这个方法就是用来测量宽高的. 被 {@link #measure(int, int)} 调用,
         * 可以被子类覆写来提供有效和准确的测量数据     
         * 注意:当重写这个方法的时候,请必须调用 {@link #setMeasuredDimension(int, int)}  来存储测量到的宽高
         *  否则,会由  {@link #measure(int, int)} 抛出 IllegalStateException 异常
         * 如果这个方法被重写,由子类负责确保宽高起码是View 的最小宽高 
         */
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
        public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
    
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }
    

    这里的onMeasure 只是缺省定义,对于ViewGroup 来说有不同的Layout,有LinearLayout、RelativeLayout、FrameLayout 等,这些的 onMeasure 都由各自实现,在ViewGroup中定义了 measureChild 、 measureChildWithMargins 方法来对子视图进行测量,我们看到这两个方法都是先调用 getChildMeasureSpec 然后再调用子View 去 measure。而 measure 中真正起作用的是 onMeasure 方法,即各个layout 中的 onMeasure 方法

        protected void measureChildWithMargins(View child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
            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(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    

    getChildMeasureSpec 通过参数传递的 MeasureSpec 得到 SpecMode 和 SpecSize, 然后根据计算出来的 SpecMode 和 参数 childDimension,来得到子 View 的 resultSize、resultMode 并计算出子 View 的MeasureSpec 。

    onLayout

    View 和 ViewGroup 的 layout 方法是有区别的,ViewGroup 的 layout 有final,即不可被继承,所以,View 可以重写 layout 和 onLayout 方法,这两个方法的参数只有一个区别 boolean changed;而 ViewGroup 只能重写 onLayout
    所谓的 onLayout 即是根据 onMeasure 中测量的结果和一些padding、margin 来进行子View 的layout,具体的作用查看下面注释

        /**
         * Assign a size and position to a view and all of its
         * descendants
         *
         * <p>This is the second phase of the layout mechanism.
         * (The first is measuring). In this phase, each parent calls
         * layout on all of its children to position them.
         * This is typically done using the child measurements
         * that were stored in the measure pass().</p>
         *
         * 下面注释中提到子类不应该重写这个方法
         * <p>Derived classes should not override this method.
         * Derived classes with children should override
         * onLayout. In that method, they should
         * call layout on each of their children.</p>
         *
         * @param l Left position, relative to parent
         * @param t Top position, relative to parent
         * @param r Right position, relative to parent
         * @param b Bottom position, relative to parent
         */
        @SuppressWarnings({"unchecked"})
        public void layout(int l, int t, int r, int b) {
            ...
            boolean changed = isLayoutModeOptical(mParent) ?
                    setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
            ...
        }
    

    onDraw

        /**
         * Manually render this view (and all of its children) to the given Canvas.
         * The view must have already done a full layout before this function is
         * called.  When implementing a view, implement
         * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
         * If you do need to override this method, call the superclass version.
         *
         * @param canvas The Canvas to which the View is rendered.
         */
        @CallSuper
        public void draw(Canvas canvas) {
           ...
            /*
             * 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)
           ...
                // Step 3, draw the content
                if (!dirtyOpaque) onDraw(canvas);
    
                // Step 4, draw the children
                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;
           ...
            // Step 2, save the canvas' layers
            int paddingLeft = mPaddingLeft;
           ...
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);
    
            // Step 4, draw the children
            dispatchDraw(canvas);
    
            // Step 5, draw the fade effect and restore layers
            final Paint p = scrollabilityCache.paint;
            final Matrix matrix = scrollabilityCache.matrix;
            final Shader fade = scrollabilityCache.shader;
           ...
            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);
    
            if (debugDraw()) {
                debugDrawFocus(canvas);
            }
    }
    

    这里有两个重要方法 onDraw 和 dispatchDraw,在 onDrawForegroud 中还有一个 onDrawScrollbars,所以draw的调用栈是 draw -> onDraw | dispatchDraw | onDrawScrollBars, 具体 View 和ViewGroup 会有区别

    invalidate 和 requestLayout

    view的invalidate不会导致 ViewRootImpl 的 invalidate 被调用,而是递归调用父 view 的invalidateChildInParent,直到 ViewRootImpl 的 invalidateChildInParent,然后触发 peformTraversals,会导致当前 view 被重绘,由于 mLayoutRequested 为 false,不会导致 onMeasure 和 onLayout 被调用,而 OnDraw 会被调用

    requestLayout 会直接递归调用父窗口的 requestLayout,直到 ViewRootImpl ,然后触发 peformTraversals ,由于 mLayoutRequested 为 true,会导致onMeasure 和 onLayout 被调用。不一定会触发OnDraw。如果触发了 onDraw 可能是因为在 layout 过程中发现l,t,r,b和以前不一样,那就会触发一次 invalidate,所以触发了onDraw,也可能是因为别的原因导致 mDirty 非空(比如在跑动画)

    一般来说,只要刷新的时候就调用 invalidate,需要重新 measure 就调用requestLayout,后面再跟个invalidate(为了保证重绘)

    RelativeLayout 和 LinearLayout 的 measure 对比

    下面我们来看一下 RelativeLayout 和LinearLayout 的具体实现
    首先是 LinearLayout 的 onMeasure -> measureVertical

        void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
            ...
            // 主要是计算最大高度,顺便了解下最大宽度
            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                ...
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                totalWeight += lp.weight;
    
                // 注意1,如果 (height == 0 && weight > 0)
                final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
                if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
                    // 优化操作: 即使子View使用的空间过量,对子View的测量也是没有影响的
                    // 因为稍后会重新测量
                    if (isExactly) {
                        mTotalLength += lp.leftMargin + lp.rightMargin;
                    } else {
                        final int totalLength = mTotalLength;
                        mTotalLength = Math.max(totalLength, totalLength +
                                lp.leftMargin + lp.rightMargin);
                    }
                    if (baselineAligned) {
                        final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec(
                                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
                        final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec(
                                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
                        child.measure(freeWidthSpec, freeHeightSpec);
                    } else {
                        skippedMeasure = true;
                    }
                } else {
                    if (useExcessSpace) {
                        // heightMode 要么为UNSPECIFIED,要么为 AT_MOST。这里暂时使用WRAP_CONTENT表示,以便稍后能得到最佳的结果
                        lp.height = LayoutParams.WRAP_CONTENT;
                    }
    
                    // 根据weight 分配全部高度或没有高度
                    final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
                    measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                            heightMeasureSpec, usedHeight);
    
                    final int childHeight = child.getMeasuredHeight();
                    if (useExcessSpace) {
                        // 还记得前面我们把 height 设置为 WRAP_CONTENT么,这里我们把height 恢复 为 0
                        // 这样才能匹配EXACTLY 测量模式
                        // 与上面的相对应
                        lp.height = 0;
                        consumedExcessSpace += childHeight;
                    }
    
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                           lp.bottomMargin + getNextLocationOffset(child));
    
                    if (useLargestChild) {
                        largestChildHeight = Math.max(childHeight, largestChildHeight);
                    }
                }
    
                ...
                if (lp.weight > 0) {
                    /*
                     *  因为宽的值是假的,我们需要让它们保持独立
                     */
                    weightedMaxWidth = Math.max(weightedMaxWidth,
                            matchWidthLocally ? margin : measuredWidth);
                } else {
                    alternativeMaxWidth = Math.max(alternativeMaxWidth,
                            matchWidthLocally ? margin : measuredWidth);
                }
    
                i += getChildrenSkipCount(child, i);
            } // 第一次遍历结束
    
            if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
                mTotalLength += mDividerHeight;
            }
    
            // useLargestChild 代表一种模式,当设置为 true 的时候,所有的设置了 weight 的子view都会按照最大子View的大小测量
            // 如果高度的模式是以下两种
            // 我们需要重新计算一下最大高度
            if (useLargestChild &&
                    (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
                mTotalLength = 0;
    
                for (int i = 0; i < count; ++i) {
                    ...
                    final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                            child.getLayoutParams();
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                            lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
                }
            }
            ...
            // 根据子View的 weight 扩展或收缩大小,
            // 如果他们超出了边界,也就是之前的优化操作,
            // 我们需要重新测量
            int remainingExcess = heightSize - mTotalLength
                    + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
            if (skippedMeasure
                    || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
                float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
    
                mTotalLength = 0;
    
                for (int i = 0; i < count; ++i) {
                    final View child = getVirtualChildAt(i);
                    ...
    
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    final float childWeight = lp.weight;
                    if (childWeight > 0) {
                        ...
                        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                                Math.max(0, childHeight), MeasureSpec.EXACTLY);
                        final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
                                lp.width);
                        // 这里就是第二次测量
                        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    
                        // Child may now not fit in vertical dimension.
                        childState = combineMeasuredStates(childState, child.getMeasuredState()
                                & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
                    }
    
                    ...
                    allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
    
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                            lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
                }
    
                mTotalLength += mPaddingTop + mPaddingBottom;
            } 
            ...
            setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                    heightSizeAndState);
    
            if (matchWidth) {
                forceUniformWidth(count, heightMeasureSpec);
            }
        }
    

    RelativeLayout.java

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            ...
            //第一次测量
            for (int i = 0; i < count; i++) {
                View child = views[i];
                if (child.getVisibility() != GONE) {
                    LayoutParams params = (LayoutParams) child.getLayoutParams();
                    int[] rules = params.getRules(layoutDirection);
    
                    applyHorizontalSizeRules(params, myWidth, rules);
                    measureChildHorizontal(child, params, myWidth, myHeight); 
    
                    if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                        offsetHorizontalAxis = true;
                    }
                }
            }
    
            views = mSortedVerticalChildren;
            count = views.length;
            final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
            // 第二次测量
            for (int i = 0; i < count; i++) {
                final View child = views[i];
                if (child.getVisibility() != GONE) {
                    final LayoutParams params = (LayoutParams) child.getLayoutParams();
    
                    applyVerticalSizeRules(params, myHeight, child.getBaseline());
                    measureChild(child, params, myWidth, myHeight); 
                    if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                        offsetVerticalAxis = true;
                    }
    
                    if (isWrapContentWidth) {
                        if (isLayoutRtl()) {
                            if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                                width = Math.max(width, myWidth - params.mLeft);
                            } else {
                                width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
                            }
                        } else {
                            if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                                width = Math.max(width, params.mRight);
                            } else {
                                width = Math.max(width, params.mRight + params.rightMargin);
                            }
                        }
                    }
    
                    if (isWrapContentHeight) {
                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                            height = Math.max(height, params.mBottom);
                        } else {
                            height = Math.max(height, params.mBottom + params.bottomMargin);
                        }
                    }
    
                    if (child != ignore || verticalGravity) {
                        left = Math.min(left, params.mLeft - params.leftMargin);
                        top = Math.min(top, params.mTop - params.topMargin);
                    }
    
                    if (child != ignore || horizontalGravity) {
                        right = Math.max(right, params.mRight + params.rightMargin);
                        bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                    }
                }
            }
    
            // 先找到最 Top,最 Start 的View 作为 baseline。稍后再使用 RTL 模式
            View baselineView = null;
            LayoutParams baselineParams = null;
            for (int i = 0; i < count; i++) {
                final View child = views[i];
                if (child.getVisibility() != GONE) {
                    final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
                    if (baselineView == null || baselineParams == null
                            || compareLayoutPosition(childParams, baselineParams) < 0) {
                        baselineView = child;
                        baselineParams = childParams;
                    }
                }
            }
            mBaselineView = baselineView;
    
            if (isWrapContentWidth) {
                // 宽度中已经包含了左padding,因为它的计算方式就是通过观测每个它右边的子View
                width += mPaddingRight;
    
                if (mLayoutParams != null && mLayoutParams.width >= 0) {
                    width = Math.max(width, mLayoutParams.width);
                }
    
                width = Math.max(width, getSuggestedMinimumWidth());
                width = resolveSize(width, widthMeasureSpec);
    
                if (offsetHorizontalAxis) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            final int[] rules = params.getRules(layoutDirection);
                            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                                centerHorizontal(child, params, width);
                            } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
                                final int childWidth = child.getMeasuredWidth();
                                params.mLeft = width - mPaddingRight - childWidth;
                                params.mRight = params.mLeft + childWidth;
                            }
                        }
                    }
                }
            }
    
            if (isWrapContentHeight) {
                // Height already has top padding in it since it was calculated by looking at
                // the bottom of each child view
                height += mPaddingBottom;
    
                if (mLayoutParams != null && mLayoutParams.height >= 0) {
                    height = Math.max(height, mLayoutParams.height);
                }
    
                height = Math.max(height, getSuggestedMinimumHeight());
                height = resolveSize(height, heightMeasureSpec);
    
                if (offsetVerticalAxis) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            final int[] rules = params.getRules(layoutDirection);
                            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                                centerVertical(child, params, height);
                            } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
                                final int childHeight = child.getMeasuredHeight();
                                params.mTop = height - mPaddingBottom - childHeight;
                                params.mBottom = params.mTop + childHeight;
                            }
                        }
                    }
                }
            }
    
            if (horizontalGravity || verticalGravity) {
                final Rect selfBounds = mSelfBounds;
                selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
                        height - mPaddingBottom);
    
                final Rect contentBounds = mContentBounds;
                Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
                        layoutDirection);
    
                final int horizontalOffset = contentBounds.left - left;
                final int verticalOffset = contentBounds.top - top;
                if (horizontalOffset != 0 || verticalOffset != 0) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE && child != ignore) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            if (horizontalGravity) {
                                params.mLeft += horizontalOffset;
                                params.mRight += horizontalOffset;
                            }
                            if (verticalGravity) {
                                params.mTop += verticalOffset;
                                params.mBottom += verticalOffset;
                            }
                        }
                    }
                }
            }
    
            if (isLayoutRtl()) {
                final int offsetWidth = myWidth - width;
                for (int i = 0; i < count; i++) {
                    final View child = views[i];
                    if (child.getVisibility() != GONE) {
                        final LayoutParams params = (LayoutParams) child.getLayoutParams();
                        params.mLeft -= offsetWidth;
                        params.mRight -= offsetWidth;
                    }
                }
            }
    
            setMeasuredDimension(width, height);
        }
    

    所以结论就是LinearLayout在使用weight属性进行布局时会对子View进行两次measure
    RelativeLayout 会根据2次排列的结果对子View各做一次measure。因为 RelativeLayout 中子View的排列方式是基于彼此的依赖关系,而这个依赖关系可能和Xml布局中View的顺序不同,在确定每个子View的位置的时候,需要先给所有的子View排序一下。又因为RelativeLayout允许ViewB在横向上依赖ViewA,ViewA在纵向上依赖B。所以需要横向纵向分别进行一次排序测量

    相关文章

      网友评论

          本文标题:android View 绘制

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