Viewmeasure函数

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            // first clears the measured dimension flag            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();            // measure ourselves, this should set the measured dimension flag back            onMeasure(widthMeasureSpec, heightMeasureSpec);            // 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("onMeasure() did not set the"                        + " measured dimension by calling"                        + " setMeasuredDimension()");            }            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;}

参数widthMeasureSpec和heightMeasureSpec由父View构建,表示父View给子View的测量要求,这个值就是根据XML里面设置的View的layout_width和layout_height来构建的(其实不只是这样的),总之,这两个参数是父布局传给子View的。

DecorView根布局的参数widthMeasureSpec和heightMeasureSpec在代码里面初始化的时候给出。

onMeasure的默认实现

// widthMeasureSpec和heightMeasureSpec 为父类传过来的宽高。    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }

getDefaultSize实现

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:// wrap_content会封装成AT_MOST  具体的精确值或者match_parent会封装成EXACTLY(后面会看到)//因此可以看出默认的测量实现中设置成包裹内容(wrap_content)和精确值都会将宽高设置成MeasureSpec中封装的size值            result = specSize;            break;        }        return result;    }

ViewGroup:View的measure的参数就是父布局传过来的,而ViewGroup又是继承自View的,那么他的measure的参数其实也是父类传过来的。DecorView根布局的参数widthMeasureSpec和heightMeasureSpec在代码里面初始化的时候给出。

//某个ViewGroup类型的视图  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    //必须调用super.ononMeasure()或者直接调用setMeasuredDimension()方法设置该View大小,否则会报异常。    super.onMeasure(widthMeasureSpec , heightMeasureSpec)       //setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),       //        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));           //遍历每个子View    for(int i = 0 ; i < getChildCount() ; i++){      View child = getChildAt(i);  //调用子View的onMeasure,默认的情况又回到了上面的实现。//这个只是示意一下大致的逻辑    child.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec);    }  }

ViewGroup提供了measureChildren来进行测量

//widthMeasureSpec 和  heightMeasureSpec 表示该父View的布局要求  //遍历每个子View,然后调用measureChild()方法去实现每个子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) { // 不处于 “GONE” 状态              measureChild (child, widthMeasureSpec, heightMeasureSpec);          }  
//测量每个子View高宽时,清楚了该View本身的边距大小,即android:padding属性 或android:paddingLeft等属性标记  protected void measureChild(View child, int parentWidthMeasureSpec,          int parentHeightMeasureSpec) {      final LayoutParams lp = child.getLayoutParams(); // LayoutParams属性      //设置子View的childWidthMeasureSpec属性,去除了该父View的边距值  mPaddingLeft + mPaddingRight      final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,              mPaddingLeft + mPaddingRight, lp.width);      //设置子View的childHeightMeasureSpec属性,去除了该父View的边距值  mPaddingTop + mPaddingBottom      final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,              mPaddingTop + mPaddingBottom, lp.height);    //可以很清楚的看到传递给子View的两个参数是由父类确定的,由getChildMeasureSpec得到的。    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  }

还有和measureChild类似的函数,比如measureChildWithMargins

protected void measureChildWithMargins(View child,            int parentWidthMeasureSpec, int widthUsed,            int parentHeightMeasureSpec, int heightUsed) {        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();        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// spec参数表示该父View本身所占的widthMeasureSpec 或  heightMeasureSpec值  // padding参数表示该父View的边距大小,见于android:padding属性 或android:paddingLeft等属性标记  // childDimension参数表示该子View内部LayoutParams属性的值,可以是wrap_content、match_parent、一个精确指(an exactly size),   public static int getChildMeasureSpec(int spec, int padding, int childDimension) {      int specMode = MeasureSpec.getMode(spec);  //获得父View的mode      int specSize = MeasureSpec.getSize(spec);  //获得父View的实际值        int size = Math.max(0, specSize - padding); //父View为子View设定的大小,减去边距值,        int resultSize = 0;     int resultMode = 0;       switch (specMode) {      // Parent has imposed an exact size on us      //1、父View是EXACTLY的 !      case MeasureSpec.EXACTLY:           //1.1、子View的width或height是个精确值 (an exactly size)          if (childDimension >= 0) {                        resultSize = childDimension;         //size为精确值              resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。          }           //1.2、子View的width或height为 MATCH_PARENT/FILL_PARENT           else if (childDimension == LayoutParams.MATCH_PARENT) {              // Child wants to be our size. So be it.              resultSize = size;                   //size为父视图大小              resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。          }           //1.3、子View的width或height为 WRAP_CONTENT          else if (childDimension == LayoutParams.WRAP_CONTENT) {              // Child wants to determine its own size. It can't be              // bigger than us.              resultSize = size;                   //size为父视图大小              resultMode = MeasureSpec.AT_MOST;    //mode为AT_MOST 。          }          break;        // Parent has imposed a maximum size on us      //2、父View是AT_MOST的 !          case MeasureSpec.AT_MOST:          //2.1、子View的width或height是个精确值 (an exactly size)          if (childDimension >= 0) {              // Child wants a specific size... so be it              resultSize = childDimension;        //size为精确值              resultMode = MeasureSpec.EXACTLY;   //mode为 EXACTLY 。          }          //2.2、子View的width或height为 MATCH_PARENT/FILL_PARENT          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;                  //size为父视图大小              resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST          }          //2.3、子View的width或height为 WRAP_CONTENT          else if (childDimension == LayoutParams.WRAP_CONTENT) {              // Child wants to determine its own size. It can't be              // bigger than us.              resultSize = size;                  //size为父视图大小              resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST          }          break;        // Parent asked to see how big we want to be      //3、父View是UNSPECIFIED的 !      case MeasureSpec.UNSPECIFIED:          //3.1、子View的width或height是个精确值 (an exactly size)          if (childDimension >= 0) {              // Child wants a specific size... let him have it              resultSize = childDimension;        //size为精确值              resultMode = MeasureSpec.EXACTLY;   //mode为 EXACTLY          }          //3.2、子View的width或height为 MATCH_PARENT/FILL_PARENT          else if (childDimension == LayoutParams.MATCH_PARENT) {              // Child wants to be our size... find out how big it should              // be              resultSize = 0;                        //size为0! ,其值未定              resultMode = MeasureSpec.UNSPECIFIED;  //mode为 UNSPECIFIED          }           //3.3、子View的width或height为 WRAP_CONTENT          else if (childDimension == LayoutParams.WRAP_CONTENT) {              // Child wants to determine its own size.... find out how              // big it should be              resultSize = 0;                        //size为0! ,其值未定              resultMode = MeasureSpec.UNSPECIFIED;  //mode为 UNSPECIFIED          }          break;      }      //根据上面逻辑条件获取的mode和size构建MeasureSpec对象。      return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  } 

getChildMeasureSpec(int spec, int padding, int childDimension)函数表示传递给子ViewonMeasure函数中的参数是由父布局和子布局共同决定的。

Spec参数表示父View本身的widthMeasureSpecheightMeasureSpec值,

childDimension参数表示该子View内部LayoutParams属性的值,可以是wrap_contentmatch_parent、一个精确值(an exactly size)

最外层的ViewonMeasure函数中的第一个参数是在代码中初始化时给出的

childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);  childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);

private int getRootMeasureSpec(int windowSize, int rootDimension) {      int measureSpec;      switch (rootDimension) {      case ViewGroup.LayoutParams.MATCH_PARENT:          measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);          break;      case ViewGroup.LayoutParams.WRAP_CONTENT:          measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);          break;      default:          measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);          break;      }      return measureSpec;  }  

使用了MeasureSpec.makeMeasureSpec()方法来组装一个MeasureSpec,当rootDimension参数等于MATCH_PARENT的时候,MeasureSpec的specMode就等于EXACTLY,当rootDimension等于WRAP_CONTENT的时候,MeasureSpec的specMode就等于AT_MOST。并且MATCH_PARENT和WRAP_CONTENT时的specSize都是等于windowSize的,也就意味着根视图总是会充满全屏的。


lp.width是MATCH_PARENT,因此测量得到的measureSpec为屏幕大小的EXACTLY,也就是说,最顶层的View的onMeasure函数里面的参数为屏幕尺寸是EXACTLY的。


经过上面的分析可以看出果然是:传递给ViewonMeasurewidthMeasureSpecheightMeasureSpec值是经过父布局计算的。

getChildMeasureSpec函数的实际意义如下图所示:



参考文章:

http://blog.csdn.net/fengye810130/article/details/9181531
http://blog.csdn.net/guolin_blog/article/details/16330267



更多相关文章

  1. Android(安卓)属性动画中心点无限循环
  2. Android(安卓)剪切板ClipboardManager过时问题
  3. android camera根据屏幕图像大小设置显示
  4. Android(Java):Android(安卓)jni学习(二)
  5. ADT-abundle-使用过程中不断出现的错误
  6. AndroidManifest.xml文件详解(supports-screens)
  7. 出错:PhoneFactory.getDefaultPhone must be called from Looper
  8. mybatisplus的坑 insert标签insert into select无参数问题的解决
  9. Python技巧匿名函数、回调函数和高阶函数

随机推荐

  1. AndroidStudio中使用Kotlin--实现接口方
  2. Android(安卓)Studio 入门:(五) 悬浮按钮增
  3. 知识网站
  4. Android音乐播放器【支持:速率调节,音调调
  5. Android(安卓)官方数据绑定 Data Binding
  6. Android之微信开放平台创建应用
  7. Android(安卓)获取图库中图片的坑
  8. Android(安卓)官方 switch 控件更改选中
  9. 弹窗之三:自定义Dialog
  10. 移动开发:Android官方提供的支持不同屏幕