源码路径 frameworks\base\core\java\android\view\View.java

源码中国链接:http://www.oschina.net/code/explore/android-2.2-froyo/android/view/View.java

[java] view plain copy
  1. publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){
  2. if((mPrivateFlags&FORCE_LAYOUT)==FORCE_LAYOUT||
  3. widthMeasureSpec!=mOldWidthMeasureSpec||
  4. heightMeasureSpec!=mOldHeightMeasureSpec){
  5. //firstclearsthemeasureddimensionflag
  6. mPrivateFlags&=~MEASURED_DIMENSION_SET;
  7. if(ViewDebug.TRACE_HIERARCHY){
  8. ViewDebug.trace(this,ViewDebug.HierarchyTraceType.ON_MEASURE);
  9. }
  10. //measureourselves,thisshouldsetthemeasureddimensionflagback
  11. onMeasure(widthMeasureSpec,heightMeasureSpec);
  12. //flagnotset,setMeasuredDimension()wasnotinvoked,weraise
  13. //anexceptiontowarnthedeveloper
  14. if((mPrivateFlags&MEASURED_DIMENSION_SET)!=MEASURED_DIMENSION_SET){
  15. thrownewIllegalStateException("onMeasure()didnotsetthe"
  16. +"measureddimensionbycalling"
  17. +"setMeasuredDimension()");
  18. }
  19. mPrivateFlags|=LAYOUT_REQUIRED;
  20. }
  21. mOldWidthMeasureSpec=widthMeasureSpec;
  22. mOldHeightMeasureSpec=heightMeasureSpec;
  23. }
可以看到measure函数有2个参数,widthMeasureSpec 和 heightMeasureSpec。我最初的疑问是不知道该怎么传这两个参数,于是跟到源码里面看看。这个函数的工作大概如下:

(mPrivateFlags这个还没研究,先跳过了)

1.检查传入的widthMeasureSpec和heightMeasureSpec是否与当前的值是一样的,不一样的话,调用onMeasure函数,并设置mPrivateFlags。

2.保存新值到mOldWidthMeasureSpec和mOldHeightMeasureSpec。这两个变量不用深究了,没有其他地方用到,就只是在这个函数中用来比较值用的。

3.这里判断符合条件后会抛出一个IllegalStateException的异常,它的提示信息很清楚,告诉我们要调用setMeasuredDimension()方法。但到底是怎么回事呢?这是在你需要重写onMeasure函数时需要注意的。

先来看看默认的View的onMeasure函数吧:

[java] view plain copy
  1. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec));
  4. }
当我们需要重写onMeasure时,记得要调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight,否则,就会抛出上面那个异常哦~

继续来看setMeasuredDimension:

[java] view plain copy
  1. protectedfinalvoidsetMeasuredDimension(intmeasuredWidth,intmeasuredHeight){
  2. mMeasuredWidth=measuredWidth;
  3. mMeasuredHeight=measuredHeight;
  4. mPrivateFlags|=MEASURED_DIMENSION_SET;
  5. }
哦,很简单,就是设置了mMeasuredWidth和mMeasuredHeight,然后给mPrivateFlags设置了MEASURED_DIMENSION_SET标志位。那么计算都是在getDefaultSize函数里实现的: [java] view plain copy
  1. publicstaticintgetDefaultSize(intsize,intmeasureSpec){
  2. intresult=size;
  3. intspecMode=MeasureSpec.getMode(measureSpec);
  4. intspecSize=MeasureSpec.getSize(measureSpec);
  5. switch(specMode){
  6. caseMeasureSpec.UNSPECIFIED:
  7. result=size;
  8. break;
  9. caseMeasureSpec.AT_MOST:
  10. caseMeasureSpec.EXACTLY:
  11. result=specSize;
  12. break;
  13. }
  14. returnresult;
  15. }

看到了一个MeasureSpec,看来主要工作是在这里,必须得进去看看了。

[java] view plain copy
  1. publicstaticclassMeasureSpec{
  2. privatestaticfinalintMODE_SHIFT=30;
  3. privatestaticfinalintMODE_MASK=0x3<<MODE_SHIFT;
  4. publicstaticfinalintUNSPECIFIED=0<<MODE_SHIFT;
  5. publicstaticfinalintEXACTLY=1<<MODE_SHIFT;
  6. publicstaticfinalintAT_MOST=2<<MODE_SHIFT;
  7. publicstaticintmakeMeasureSpec(intsize,intmode){
  8. returnsize+mode;
  9. }
  10. publicstaticintgetMode(intmeasureSpec){
  11. return(measureSpec&MODE_MASK);
  12. }
  13. publicstaticintgetSize(intmeasureSpec){
  14. return(measureSpec&~MODE_MASK);
  15. }
  16. }

类不大,就都贴出来了,为了精简篇幅,去掉了注释和toString函数。

这里MODE_MASK二进制是11000(一共30个0)00,也就是最高2位标识mode,其余位标识size。

接下来回到getDefaultSize函数

通过这个类的方法从参数measureSpec中提取出了specMode和specSize。 specMode的作用在下面的switch语句中可以看出来。

[java] view plain copy
  1. caseMeasureSpec.UNSPECIFIED:
  2. result=size;
  3. break;
这里的size就是getSuggestedMinimumWidth()或者getSuggestedMinimumHeight(),是一个默认的最小宽或高,可以看到如果specMode为MeasureSpec.UNSPECIFIED时,specSize(即我们希望设置的size)是没有用到的。
[java] view plain copy
  1. caseMeasureSpec.AT_MOST:
  2. caseMeasureSpec.EXACTLY:
  3. result=specSize;
  4. break;
当specMode为MeasureSpec.AT_MOST或MeasureSpec.EXACTLY时,从我们传入的参数measureSpec中提取出来的specSize被采用了。这种情况下上面的size就被废弃了。当result确定后,就是setMeasuredDimension被调用了,在里面将会对mMeasuredWidth和mMeasuredHeight进行设置。

简单示例:

OK,现在应该理解了吧,下面是一个调用measure方法的示例:
[java] view plain copy
  1. mTextView.measure(MeasureSpec.EXACTLY+mTextView.getWidth(),MeasureSpec.EXACTLY);
  2. mTextView.layout(0,0,mTextView.getMeasuredWidth(),mTextView.getMeasuredHeight());
把mode标志和你想设置的大小相加,传进去就OK啦。这里设置height的时候我是想设0,因此直接传了MeasureSpec.EXACTLY进去。

当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。按示例调用layout函数后,View的大小将会变成你想要设置成的大小。

另外关于layout,包括整个布局流程,我将要写另一篇博文介绍。因此在这里就不再赘述了。

更多相关文章

  1. C语言函数以及函数的使用
  2. android 调用draw(canvas) 函数自动退出
  3. 解析ANDROID ps命令执行后各项参数的含义
  4. Xposed框架之函数Hook学习
  5. Flutter下载更新App的方法示例
  6. Android之——多线程断点续传下载示例
  7. Android/iOS内嵌Unity开发示例
  8. 布局中文件中【控件间距参数详解以及单位选择】

随机推荐

  1. Android一些网站介绍
  2. Android中,把XML文件转换成Object对象的方
  3. android ndk编译getevent
  4. Android(安卓)MediaCodec踩坑笔记
  5. Android开发利器之Data Binding Compiler
  6. Android应用程序组件Content Provider在
  7. android SQLite操作
  8. Porting WiFi drivers to Android
  9. RecyclerView正确打开分隔符
  10. Android Manifest之元素中文注释