美文网首页
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