View

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

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

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            // first clears the measured dimension flag            mPrivateFlags &= ~MEASURED_DIMENSION_SET;            if (ViewDebug.TRACE_HIERARCHY) {                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);            }            // 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 & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {                throw new IllegalStateException("onMeasure() did not set the"                        + " measured dimension by calling"                        + " setMeasuredDimension()");            }            mPrivateFlags |= LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;    }
可以看到measure函数有2个参数,widthMeasureSpec 和 heightMeasureSpec。我最初的疑问是不知道该怎么传这两个参数,于是跟到源码里面看看。这个函数的工作大概如下:

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

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

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

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

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

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }
当我们需要重写onMeasure时,记得要调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight,否则,就会抛出上面那个异常哦~

继续来看setMeasuredDimension:

    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {        mMeasuredWidth = measuredWidth;        mMeasuredHeight = measuredHeight;        mPrivateFlags |= MEASURED_DIMENSION_SET;    }
哦,很简单,就是设置了mMeasuredWidth和mMeasuredHeight,然后给mPrivateFlags设置了MEASURED_DIMENSION_SET标志位。那么计算都是在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:            result = specSize;            break;        }        return result;    }

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

    public static class MeasureSpec {        private static final int MODE_SHIFT = 30;        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;        public static final int UNSPECIFIED = 0 << MODE_SHIFT;        public static final int EXACTLY     = 1 << MODE_SHIFT;        public static final int AT_MOST     = 2 << MODE_SHIFT;        public static int makeMeasureSpec(int size, int mode) {            return size + mode;        }        public static int getMode(int measureSpec) {            return (measureSpec & MODE_MASK);        }        public static int getSize(int measureSpec) {            return (measureSpec & ~MODE_MASK);        }    }

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

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

接下来回到getDefaultSize函数

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

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

简单示例:

OK,现在应该理解了吧,下面是一个调用measure方法的示例:

mTextView.measure(MeasureSpec.EXACTLY + mTextView.getWidth(), MeasureSpec.EXACTLY);mTextView.layout(0, 0, mTextView.getMeasuredWidth(), mTextView.getMeasuredHeight());
把mode标志和你想设置的大小相加,传进去就OK啦。这里设置height的时候我是想设0,因此直接传了MeasureSpec.EXACTLY进去。

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

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


更多相关文章

  1. editText设置最大长度
  2. android布局属性大全
  3. android 4.0.3 设置时间无法更新到RTC
  4. 刚学会百度地图最新版的sdk,总结一个简单的demo
  5. [Android] 环境配置之Android(安卓)Studio开发NDK
  6. widget(1、TextView)
  7. Unity调用Android录音
  8. android wifi子系统
  9. Unity调用Android录音

随机推荐

  1. Gradle在Android(安卓)Studio 的应用
  2. Android 服务的五大级别
  3. 2011.09.23——— android sample之Notep
  4. android——xml drawable实现背景样式
  5. android之button
  6. Android P Launcher显示ALL APP ICON 和
  7. Run native executable in Android App
  8. android listview onItemClick不执行
  9. Android_嵌入chrome浏览器
  10. 从J2EE转向Android的第九天-----文件存储