美文网首页Android知识
ViewGroup的measure

ViewGroup的measure

作者: 一个冬季 | 来源:发表于2017-11-12 15:36 被阅读29次

我们平常见的最多的这个ViewGroup,都是通过Linlayout和Relayout来通过继承ViewGroup的方式
ViewGroup绘制布局呢,它会遍历所有的子View

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
               //这里是绘制的
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

我们点击measureChild方法看看

 protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

这里的这部分代码我们之前讲解 Measure 基础学习的时候,我就有讲过。
我们ViewGroup是一个抽象类,然后它具体怎么去测量的过程并没有写在ViewGroup里面,它是写在我们Linlayout、Relayout里面的。我们今天就只看Linlayout的一个具体的测量过程
所以我们直接去Linalyout的onMeasure方法查看

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {//竖直的方向
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

如果方向是Vertical就采用第一种,否则就是第二种
我们直接来看第一个的好了。
measureVertical:

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
         ....这里省略非常多的代码
        for (int i = 0; i < count; ++i) { //子布局的大小开始测量
             ....省略代码
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            totalWeight += lp.weight;
            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) {
                    lp.height = LayoutParams.WRAP_CONTENT;
                }
                final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
            这里我们需要注意,按我们翻译过来的意思就是,测量之前需要布局,这部分我们下一篇会提到
                measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);

                final int childHeight = child.getMeasuredHeight();
         ....省略代码了
      }//for循环结束
    
        //这里开始测量Llinlayout自己的大小了
        //padding内边距的相加 ,
        mTotalLength += mPaddingTop + mPaddingBottom;
         int heightSize = mTotalLength;
     //=================
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
       int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
//==========这里就有点类似View里面的测量方式了,都是去取最大的值
//getSuggestedMinimumHeight()这个方法跟View里面的getSuggestedMinimumHeight()一样的,
看是否有背景大小,然后再看最低的高度是多少(android:MinHeight),
让背景大小跟minHeight相互比较,取最大的那个。
//resolveSizeAndState里面呢,根据测量模式的不同,
来判断是直接获取padding的大小还是直接获取父容器给的指定空间大小,
不管它取哪个值,它最后的大小不能超过父容器给的空间的
        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;

    ......省略代码
   //这里就设置了最后的布局大小了
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);

resolveSizeAndState方法

 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
  //这里的size是paddingTop/paddingBottom的总和
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
        final int result;
 //根据测量的模式,来判断取哪个值
        switch (specMode) {
            case MeasureSpec.AT_MOST:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }

从上面我们可以知道我们最后的结果,受到父容器的measureSpec影响,和padding影响。
有时候系统可能会多次调用measure才可以正确的测量到宽高,如果我们在onMeasure过程中去获取测量的宽高,是不能够准确的,所以我们可以采用如下的方式来获取,要等他measure完毕

 public void onStart(){
       super.onStart();
  view.post(new Runnable()){
       public void run(){
          int width = view.getMeasureWidth();
          int height = view.getMeasureHeight();
      }
});
}
//我们发送一个消息到队里的尾部,当我们Looper调用此runnalber的时候,
表示view已经初始化好了一切了

学习资源书籍:《Android 开发艺术探索》

相关文章

网友评论

    本文标题:ViewGroup的measure

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