源码路径 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. Android(安卓)通过继承TextView类自定义字体默认颜色
  2. android TextView的跑马灯效果的实现
  3. Android(安卓)ListView(Selector 背景图片 全选 Checkbox等按钮)
  4. Android的权限permission
  5. android在java代码中绘制矩形框
  6. Android(安卓)按Menu弹出菜单
  7. android 权限
  8. 禁止Android的StatusBar下拉
  9. android调用系统程序

随机推荐

  1. TransitionDrawable的简单替换图片
  2. Android中的线程总结
  3. Android(安卓)SharedPreference源码浅析
  4. Android(安卓)的 Relative Layout 常量
  5. Android(安卓)在xml中配置 float 和 inte
  6. Android(安卓)SeLinux权限问题和解决方法
  7. ClassNotFoundException: Didn't find cl
  8. android仿支付宝密码输入框效果
  9. Android(安卓)Studio使用过程中遇到的问
  10. Android中互联网的应用