美文网首页
Linerlayout的(测量)源码分析

Linerlayout的(测量)源码分析

作者: 愤怒的板蓝根 | 来源:发表于2018-09-20 01:34 被阅读0次

    自定义属性 的基本流程 是从测量 -摆放 -绘制 我们直接从他的测量开始分析

      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
           //测量纵向布局
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
         //测量横向布局
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }
    

    我们先去测量纵向布局 看下

         void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
         mTotalLength = 0;
        int maxWidth = 0;
        int childState = 0;
        int alternativeMaxWidth = 0;
        int weightedMaxWidth = 0;
        boolean allFillParent = true;
        float totalWeight = 0;
    
        final int count = getVirtualChildCount();
    
        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);
            if (child == null) {
              // 如果子布局为空 则进行mTotalLength  自加  measureNullChild(i);为0
                mTotalLength += measureNullChild(i);
                continue;
            }
              //获取子布局的显示状态
            if (child.getVisibility() == View.GONE) {
               i += getChildrenSkipCount(child, i);
               continue;
            }
            nonSkippedChildCount++;
            if (hasDividerBeforeChildAt(i)) {
                mTotalLength += mDividerHeight;
            }
               //获取子布局的 LayoutParams 
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                 //不断的加
            totalWeight += lp.weight;
                  //一个判断的标志
            final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
            //如果测量模式是进准测量 
            if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
                // Optimization: don't bother measuring children who are only
                // laid out using excess space. These views will get measured
                // later if we have space to distribute.
                 //总长度的值
                final int totalLength = mTotalLength;
                   //取其中的最大值
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;
            } else {
                if (useExcessSpace) {
                    // The heightMode is either UNSPECIFIED or AT_MOST, and
                    // this child is only laid out using excess space. Measure
                    // using WRAP_CONTENT so that we can find out the view's
                    // optimal height. We'll restore the original height of 0
                    // after measurement.
                     //如果不是则给子布局一个WRAP_CONTENT;
                    lp.height = LayoutParams.WRAP_CONTENT;
                }
    
                // Determine how big this child would like to be. If this or
                // previous children have given a weight, then we allow it to
                // use all available space (and we will shrink things later
                // if needed).
                //判断已经使用的高度和总的宽度是否为0  
                final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
                 // 测量子布局 在摆放前
                measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);
                 //获取子布局的高度
                final int childHeight = child.getMeasuredHeight();
                 // 判断
                if (useExcessSpace) {
                    // Restore the original height and record how much space
                    // we've allocated to excess-only children so that we can
                    // match the behavior of EXACTLY measurement.
                    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; 
              //如果测量模式不等于 MeasureSpec.EXACTLY     并且子布局的宽度等于MATCH_PARENT
            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;
            }
              //获取宽的   margin 值
            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;
        }
    
        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;
                }
                //获取父布局的LayoutParams
                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;
        // Either expand children with weight to take up available space or
        // shrink them if they extend beyond our current bounds. If we skipped
        // measurement on any children, we need to measure them now.
        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));
                }
               //获取宽度的 margin 值
                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);
        }
    }
    

    相关文章

      网友评论

          本文标题:Linerlayout的(测量)源码分析

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