美文网首页
LinearLayout onMeasure源码阅读

LinearLayout onMeasure源码阅读

作者: 未扬帆的小船 | 来源:发表于2019-10-22 12:18 被阅读0次

    onMeasure()方法解读
    measureWithLargestChild 作用 : 该属性为true的时候, 所有带权重的子元素都会具有最大子元素的最小尺寸; 且只有当父view布局方向上的宽度或高度为wrap_content才有效

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      // 根据VERTICAL或者HORIZONTAL 分别不同的去计算相应布局
            if (mOrientation == VERTICAL) {
                measureVertical(widthMeasureSpec, heightMeasureSpec);
            } else {
                measureHorizontal(widthMeasureSpec, heightMeasureSpec);
            }
        }
    

    跟踪measureVertical()VERTICAL垂直方向的看看
    自定义View的onMeasure()方法中,最后要记得调用setMeasuredDimension()更新你计算好的大小信息等

        /**
         * Measures the children when the orientation of this LinearLayout is set
         * to {@link #VERTICAL}.
         *
         * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
         * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
         *
         * @see #getOrientation()
         * @see #setOrientation(int)
         * @see #onMeasure(int, int)
         */
        void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
            mTotalLength = 0;   //记录总长度
            int maxWidth = 0;  //最大宽
            int childState = 0;  //子View的状态
            int alternativeMaxWidth = 0; //改变源生的最大宽度
            int weightedMaxWidth = 0;  //加权最大宽度
            boolean allFillParent = true; //填充满父布局
            float totalWeight = 0; //总加权数
    
            final int count = getVirtualChildCount(); //获取子view的数量
    
            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //部分节点标识
            boolean matchWidth = false;
            boolean skippedMeasure = false;
    
            final int baselineChildIndex = mBaselineAlignedChildIndex;
            final boolean useLargestChild = mUseLargestChild;
    
            int largestChildHeight = Integer.MIN_VALUE;
            int consumedExcessSpace = 0;//消耗多余的控件
    
            int nonSkippedChildCount = 0; //没有跳过测量的孩子的总数
    
            // See how tall everyone is. Also remember max width.
            //开始遍历  看到每个控件的高度都是多高和还记得最大宽度
            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
          //如果为空 则加0 measureNullChild()返回的是 0
                if (child == null) {
                    mTotalLength += measureNullChild(i);
                    continue;
                }
          //如果为不可见 则i 加0 
                if (child.getVisibility() == View.GONE) {
                   i += getChildrenSkipCount(child, i);
                   continue;
                }
            //除去上面的判断条件
           // 下面的为没有跳过的子布局控件
                nonSkippedChildCount++;
          //hasDividerBeforeChildAt 查看又没有divider分割线在子控件前面
                if (hasDividerBeforeChildAt(i)) { 
                    mTotalLength += mDividerHeight;
                }
              //获取控件的属性信息  
                final LayoutParams lp =(LayoutParams)child.getLayoutParams();
        //下面开始根据测量模式进行逻辑运算
            //将weight信息提出来加到totalWeight 
                totalWeight += lp.weight;
          //如果高度为0 且设置了比重weight useExcessSpace= true
                final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
            // 高度为精确 且使用了比重
                if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { 
                   
    //优化:不用费心测量那些只使用多余空间(比重)的孩子。如果我们有空间分配,这些视图将在稍后测量。
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                    skippedMeasure = true; //这里设置跳过测量的标识符
                } else {
                  
                    if (useExcessSpace) {
      //高度模式不是UNSPECIFIED就是AT_MOST,并且这个孩子只是用多余的空间布置的。测量使用wrap_content以便我们可以找到视图的最佳高度。我们将恢复0的原始高度测量后
                        lp.height = LayoutParams.WRAP_CONTENT;
                    }
                    //不使用比重的情况
                    //确定这个孩子想要多大。如果这个或以前的孩子已经给了一个比重,那么我们允许它使用所有可用的空间(如果需要,我们将在以后缩小东西)。
                    final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
                    //在layout之前测量子控件
                  measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                            heightMeasureSpec, usedHeight);
    
                    final int childHeight = child.getMeasuredHeight();
                    if (useExcessSpace) {
                        //恢复原来的高度和记录我们分配多少空间excess-only孩子,这样我们可以匹配精确测量的行为。
                        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 applicable, compute the additional offset to the child's baseline
                 * we'll need later when asked {@link #getBaseline}.
                 */
                if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
                   mBaselineChildTop = mTotalLength;
                }
    
                // if we are trying to use a child index for our baseline, the above
                // book keeping only works if there are no children above it with
                // weight.  fail fast to aid the developer.
                if (i < baselineChildIndex && lp.weight > 0) {
                    throw new RuntimeException("A child of LinearLayout with index "
                            + "less than mBaselineAlignedChildIndex has weight > 0, which "
                            + "won't work.  Either remove the weight, or don't set "
                            + "mBaselineAlignedChildIndex.");
                }
    
                boolean matchWidthLocally = false;
                if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
                    // The width of the linear layout will scale, and at least one
                    // child said it wanted to match our width. Set a flag
                    // indicating that we need to remeasure at least that view when
                    // we know our width.
                    matchWidth = true;
                    matchWidthLocally = true;
                }
    
                final int margin = lp.leftMargin + lp.rightMargin;
                final int measuredWidth = child.getMeasuredWidth() + margin;
                maxWidth = Math.max(maxWidth, measuredWidth);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
    
                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
      //计算比重的大小
                if (lp.weight > 0) {
                    /*
                     * Widths of weighted Views are bogus if we end up
                     * remeasuring, so keep them separate.
                     */
                    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的时候, 所有带权重的子元素都会具有最大子元素的最小尺寸; 且只有当父view布局方向上的宽度或高度为wrap_content才有效(网上说的 我并没有验证)
            if (useLargestChild &&
                    (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
                mTotalLength = 0;
    
                for (int i = 0; i < count; ++i) {
                    final View child = getVirtualChildAt(i);
                    if (child == null) {
                        mTotalLength += measureNullChild(i);
                        continue;
                    }
    
                    if (child.getVisibility() == GONE) {
                        i += getChildrenSkipCount(child, i);
                        continue;
                    }
    
                    final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                            child.getLayoutParams();
                    // Account for negative margins
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                            lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
                }
            }
    
            // Add in our padding
            mTotalLength += mPaddingTop + mPaddingBottom;
    
            int heightSize = mTotalLength;
    
            // Check against our minimum height
            heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
    
            // Reconcile our calculated size with the heightMeasureSpec
            int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
            heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
              //要么扩大儿童比重可用空间或缩小他们是否超出我们目前的界限。如果我们跳过测量任何孩子,现在我们需要衡量他们。
    // 剩余可用空间
            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);
                    if (child == null || child.getVisibility() == View.GONE) {
                        continue;
                    }
    
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    final float childWeight = lp.weight;
                    if (childWeight > 0) {
                        final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
                        remainingExcess -= share;
                        remainingWeightSum -= childWeight;
    
                        final int childHeight;
                        if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) {
                            childHeight = largestChildHeight;
                        } else if (lp.height == 0 && (!mAllowInconsistentMeasurement
                                || heightMode == MeasureSpec.EXACTLY)) {
                            // This child needs to be laid out from scratch using
                            // only its share of excess space.
                            childHeight = share;
                        } else {
                            // This child had some intrinsic height to which we
                            // need to add its share of excess space.
                            childHeight = child.getMeasuredHeight() + share;
                        }
    
                        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));
                    }
    
                    final int margin =  lp.leftMargin + lp.rightMargin;
                    final int measuredWidth = child.getMeasuredWidth() + margin;
                    maxWidth = Math.max(maxWidth, measuredWidth);
    
                    boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
                            lp.width == LayoutParams.MATCH_PARENT;
    
                    alternativeMaxWidth = Math.max(alternativeMaxWidth,
                            matchWidthLocally ? margin : measuredWidth);
    
                    allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
    
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                            lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
                }
    
                // Add in our padding
                mTotalLength += mPaddingTop + mPaddingBottom;
                // TODO: Should we recompute the heightSpec based on the new total length?
            } else {
                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                                               weightedMaxWidth);
    
    
                // We have no limit, so make all weighted views as tall as the largest child.
                // Children will have already been measured once.
                if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
                    for (int i = 0; i < count; i++) {
                        final View child = getVirtualChildAt(i);
                        if (child == null || child.getVisibility() == View.GONE) {
                            continue;
                        }
    
                        final LinearLayout.LayoutParams lp =
                                (LinearLayout.LayoutParams) child.getLayoutParams();
    
                        float childExtra = lp.weight;
                        if (childExtra > 0) {
                            child.measure(
                                    MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
                                            MeasureSpec.EXACTLY),
                                    MeasureSpec.makeMeasureSpec(largestChildHeight,
                                            MeasureSpec.EXACTLY));
                        }
                    }
                }
            }
    
            if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
                maxWidth = alternativeMaxWidth;
            }
    
            maxWidth += mPaddingLeft + mPaddingRight;
    
            // Check against our minimum width
            maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
    
            setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                    heightSizeAndState);
    
            if (matchWidth) {
                forceUniformWidth(count, heightMeasureSpec);
            }
        }
    

    measureChildBeforeLayout()方法

      /**
        * child :当前要测量的子控件
        * totalWidth 父布局中已经被其它子控件使用的宽度
        * childIndex:当前子控件存在的下标
        * widthMeasureSpec:父控件的宽度大小参数
        * heightMeasureSpec:父控件的高度大小参数
       * totalHeight 已经使用的高度
      **/
       void measureChildBeforeLayout(View child, int childIndex,
                int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
                int totalHeight) {
            measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                    heightMeasureSpec, totalHeight);
        }
    

    measureChildWithMargins()

    /**
         *  child :当前要测量的子控件
         *  parentWidthMeasureSpec:父控件的宽度大小参数
         *  widthUsed :父布局中已经被其它子控件使用的宽度
         *  parentHeightMeasureSpec :父控件的高度大小参数
         *  heightUsed :父布局中已经被其它子控件使用的高度
         */
        protected void measureChildWithMargins(View child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        //计算获取控件的测量值
         //传入( 父控件的宽度测量值,父控件的padding左右值+Margin左右值+加上已经被使用的宽度值,本控件的宽度)
            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(int spec, int padding, int childDimension) 计算出子控件的测量参数

      /**  spec :父控件的测量信息
         * padding : 父控件padding+这个子控件的margin的信息
         * childDimension : 子控件的需要的测量大小
         **/ 
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
          //获取父控件的测量信息 --大小跟测量模式
            int specMode = MeasureSpec.getMode(spec);
            int specSize = MeasureSpec.getSize(spec);
            //剩下的空间( 父控件的最大值 -  已经用去的控件)
            int size = Math.max(0, specSize - padding);
            //待返回的结果的测量大小跟测量模式信息
            int resultSize = 0;
            int resultMode = 0;
        //  接下来重点!!!! 根据父控件的测量模式 对应求出对应的大小值
            switch (specMode) {
          // 精确模式的时候
            case MeasureSpec.EXACTLY:
            //  子控件大小>=0  
                if (childDimension >= 0) { 
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    //给最大值 填充满
                    resultSize = size;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // 子控件不能大于父控件的剩余空间
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // 父母对我们实施了一个最大尺寸
            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.
                    //子空间想要我们的规模,但我们的大小不是固定的.将剩余的空间都给予它
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    //这个跟MATCH_PARENT是一样的大小 且模式一样
                    // Child wants to determine its own size. It can't be
                    // bigger than us. 
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // 父布局未定具体大小 看子布局需要的具体大小
            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);
        }
    
    

    总结:当父布局三个模式对应的子控件三个模式的大小为:

    • MeasureSpec.EXACTLY:

    childDimension为精确值>0: 返回控件需要的大小 +MeasureSpec.EXACTLY

    LayoutParams.MATCH_PARENT:返回父布局剩下的空间+ MeasureSpec.EXACTLY

    LayoutParams.WRAP_CONTENT: 返回父布局剩下的空间+ MeasureSpec.AT_MOST

    • MeasureSpec.AT_MOST

    childDimension为精确值>0: 返回控件需要的大小 +MeasureSpec.EXACTLY

    LayoutParams.MATCH_PARENT:返回父布局剩下的空间+MeasureSpec.AT_MOST

    LayoutParams.WRAP_CONTENT: 返回父布局剩下的空间+MeasureSpec.AT_MOST

    • MeasureSpec.UNSPECIFIED
      根据sUseZeroUnspecifiedMeasureSpec决定返回的大小

    childDimension为精确值>0: 返回控件需要的大小 +MeasureSpec.EXACTLY

    LayoutParams.MATCH_PARENT: 根据sUseZeroUnspecifiedMeasureSpec决定返回的大小 0或者剩余空间大小 + MeasureSpec.UNSPECIFIED

    LayoutParams.WRAP_CONTENT: 根据sUseZeroUnspecifiedMeasureSpec决定返回的大小 0或者剩余空间大小 + MeasureSpec.UNSPECIFIED

    measure(int widthMeasureSpec, int heightMeasureSpec) 这个是用于父控件轮训调用各个子类 将子类的大小信息传入到子控件中

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
          //  这个是光学边界的判断,有没有阴影什么的那些
            boolean optical = isLayoutModeOptical(this);
            if (optical != isLayoutModeOptical(mParent)) {
                Insets insets = getOpticalInsets();
                int oWidth  = insets.left + insets.right;
                int oHeight = insets.top  + insets.bottom;
                widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
                heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
            }
    
            // Suppress sign extension for the low bytes
            //抑制符号扩展的低字节 
            long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
            if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
    
            final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
    
            // Optimize layout by avoiding an extra EXACTLY pass when the view is
            // already measured as the correct size. In API 23 and below, this
            // extra pass is required to make LinearLayout re-distribute weight.
            final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
                    || heightMeasureSpec != mOldHeightMeasureSpec;
            final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
                    && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
            final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
                    && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
            final boolean needsLayout = specChanged
                    && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
    
            if (forceLayout || needsLayout) {
                // first clears the measured dimension flag
                mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
    
                resolveRtlPropertiesIfNeeded();
    
                int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
                if (cacheIndex < 0 || sIgnoreMeasureCache) {
                    // measure ourselves, this should set the measured dimension flag back
          // 这个onMeasure()便是我们这篇文章的起点阅读,它是在measure()中调用到的 也就是说measure先与 onMeasure()
                    onMeasure(widthMeasureSpec, heightMeasureSpec);
                    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                } else {
                    long value = mMeasureCache.valueAt(cacheIndex);
                    // Casting a long to int drops the high 32 bits, no mask needed
                    setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                    mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                }
    
                // flag not set, setMeasuredDimension() was not invoked, we raise
                // an exception to warn the developer
                if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                    throw new IllegalStateException("View with id " + getId() + ": "
                            + getClass().getName() + "#onMeasure() did not set the"
                            + " measured dimension by calling"
                            + " setMeasuredDimension()");
                }
    
                mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
            }
    
            mOldWidthMeasureSpec = widthMeasureSpec;
            mOldHeightMeasureSpec = heightMeasureSpec;
    
            mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                    (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
        }
    

    总结:measure(int widthMeasureSpec, int heightMeasureSpec) 这个方法中有调用到的onMeasure()便是我们这篇文章的起点阅读,既然 它是在measure()中调用到的 也就是说measure()先于onMeasure()被调用

    相关文章

      网友评论

          本文标题:LinearLayout onMeasure源码阅读

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