美文网首页
View的measure过程详解

View的measure过程详解

作者: 空山Echo | 来源:发表于2019-05-09 17:27 被阅读0次

View的measure过程由measure方法完成,改方法是一个final类型的方法,子类不能重写,在该方法中调用onMeasure方法。下面针对View和ViewGroup讨论

View的measure过程

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中通过getDefaultSize方法根据宽高的MeasureSpec来获得宽高的测量值。
分两种情况讨论:

1. 当getDefaultSize的参数widthMeasureSpec为AT_MOST或EXACTLY时测量值就是由MeasureSpce.getSize获得的大小,即specSize:表示为父容器剩余空间的大小;

注意:关于返回值specSize 时的讨论

  • 由于获得的specSize表示为父容器剩余空间的大小,widthMeasureSpec为AT_MOST或EXACTLY时都返回此值。这与在布局中使用match_parent效果一致,但明显布局使用wrap_content时,view的尺寸并非如此
  • 通过查看源码,确实是如此。拿textview的宽度来说,它对wrap_content的情况做的特殊处理就是测量内容(字符)的长度等等,再根据计算结果和传入的宽度值做比较取小,从而得到那个初始值。
  • 所以直接继承View的自定义空间我们需要重写onMeasure方法设置wrap_content时的自身大小。方案是指定一个默认大小,没有固定依据,依情况而定,如TextView完成对内容的测量
2. 当参数为UNSPECIFIED时即返回由getSuggestedMinimumWidth方法获取的大小。(这种情况一般发生在系统内部,不用太关心)

两种情况:

  • 如果view没有设置背景,那么View的宽度为mMinWidth,即android:minWidth属性指定的值,默认为0;
  • 如果View有设置背景,那么宽度我Drawable的原始宽度,如果Drawable没有原始值则返回0;

ViewGroup的measure过程

前面分析了View的measure过程,那么ViewGroup是一个抽象类没有重写onMeasure。
通过measureChildren方法开始测量过程。

 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);
            }
        }
    }
   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);
    }

ViewGroup在measure时,会通过measureChild对每个子View进行measure。

  1. measureChild中先取出子View的LayoutParams
  2. 然后getChildMeasureSpec创建子元素的MeasureSpec
  3. 接着将MeasureSpec传递给View的measure方法进行测量

相关文章

网友评论

      本文标题:View的measure过程详解

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