android中View.measure方法詳解
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;
- }
(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));
- }
繼續來看setMeasuredDimension:
- protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
- mMeasuredWidth = measuredWidth;
- mMeasuredHeight = measuredHeight;
- mPrivateFlags |= MEASURED_DIMENSION_SET;
- }
- 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;
- case MeasureSpec.AT_MOST:
- case MeasureSpec.EXACTLY:
- result = specSize;
- break;
簡單示例:
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,包括整個佈局流程,我將要寫另一篇博文介紹。因此在這裡就不再贅述了。
相關文章
- Android中的onWindowFocusChanged()方法詳解Android
- android StartActivityForResult()方法詳解Android
- Android FragmentTabHost 使用方法詳解AndroidFragment
- Pandas中resample方法詳解
- Android中PopupWindow使用詳解Android
- Android中AsyncTask使用詳解Android
- Java中getGenericSuperclass()方法詳解Java
- Java中的main()方法詳解JavaAI
- Java中的方法引用詳解Java
- Android中SQLite應用詳解AndroidSQLite
- 詳解Android中AsyncTask的使用Android
- Android中的ANR用法詳解Android
- Android中Context用法詳解AndroidContext
- Android 中 HttpURLConnection 使用詳解AndroidHTTP
- Android中HttpURLConnection使用詳解AndroidHTTP
- Android中的Context詳解AndroidContext
- Android 中的 Checkbox 詳解Android
- Android 中的 HandlerThread 詳解Androidthread
- Android 中 XML 資料解析詳解AndroidXML
- Android中App安裝位置詳解AndroidAPP
- JQuery中$.ajax()方法引數詳解jQuery
- list中add、set方法詳解
- php中Session使用方法詳解PHPSession
- 詳解在Android中整合高德定位功能Android
- Android中Activity的LunchMode引數詳解Android
- Android中visibility屬性詳解Android
- 詳解Android中的四大元件之一:Activity詳解Android元件
- PHP 中 16 個魔術方法詳解PHP
- Python 中的魔術方法詳解Python
- JavaScript 陣列中的 indexOf 方法詳解JavaScript陣列Index
- Python中的魔術方法詳解Python
- Java 8中的default方法使用詳解Java
- Android開發中的MVP架構詳解AndroidMVP架構
- Android應用中Clean架構使用詳解Android架構
- 詳解Python中的str.format方法PythonORM
- Python 中__new__方法詳解及使用Python
- JAVA中StringBuffer類常用方法詳解Java
- jQuery Mobile中$.mobile.buttonMarkup方法使用詳解jQuery