1. 簡介
-
View的繪制過程分為三部分:measure、layout、draw。
measure用來測量View的寬和高。
layout用來計算View的位置。
draw用來繪制View。
- 本章主要對
measure過程進行詳細的分析。 - 本文源碼基于android 27。
2. measure的始點
measure是從ViewRootImpl的performTraversals()方法開始的:
2.1 ViewRootImpl的performTraversals
private void performTraversals() {
//...
//獲得view寬高的測量規(guī)格,mWidth和mHeight表示窗口的寬高,lp.widthhe和lp.height表示DecorView根布局寬和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//執(zhí)行測量
//...
}
首先會獲取view寬高的測量規(guī)格,測量規(guī)格在下一節(jié)會詳細講述,然后就是調(diào)用performMeasure()了:
2.2 ViewRootImpl的performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
//...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
//...
}
performMeasure()中就是調(diào)用View的measure()方法開始進行測量。
3.MeasureSpec
了解measure的過程時,我們須先了解MeasureSpec。MeasureSpec,顧名思義,就是測量規(guī)格,其決定了一個View的寬和高。
3.1 MeasureSpec組成
MeasureSpec代表一個32位的int值,前2位代表SpecMode,后30位代表SpecSize。其中:SpecMode代表測量的模式,SpecSize值在某種測量模式下的規(guī)格大小。來張圖解說明:

3.2 SpecMode測量模式
測量模式分為三種UNSPECIFIED、EXACTLY、AT_MOST,其具體說明如下表所示:
| 測量模式 | 說明 | 應用場景 |
|---|---|---|
| UNSPECIFIED (未指定) | 父容器沒有對當前View有任何限制,當前View可以任意取尺寸 | 系統(tǒng)內(nèi)部 |
| EXACTLY(精確) | 父容器已經(jīng)確定當前View的大小,無論View想要多大都會在這范圍內(nèi) | match_parent 和 具體數(shù)值 |
| AT_MOST(最多) | 當前View不能超過父容器規(guī)格大小,具體數(shù)值由view去決定 | wrap-content |
3.3 MeasureSpec源碼分析
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;//模式移位數(shù)
private static final int MODE_MASK = 0x3 << MODE_SHIFT;//模式掩碼
//UNSPECIFIED模式:父容器沒有對當前View有任何限制,當前View可以任意取尺寸
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
//EXACTLY模式:父容器已經(jīng)確定當前View的大小,無論View想要多大都會在這范圍內(nèi)
public static final int EXACTLY = 1 << MODE_SHIFT;
//AT_MOST模式:當前View不能超過父容器規(guī)格大小,具體數(shù)值由view去決定
public static final int AT_MOST = 2 << MODE_SHIFT;
//根據(jù)提供的size和mode得到一個測量規(guī)格
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
//sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
//即targetSdkVersion<=17時,size與mode是直接相加的;>17則進行位運算
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);//位運算
}
}
//根據(jù)提供的size和mode得到一個測量規(guī)格
public static int makeSafeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0;
}
return makeMeasureSpec(size, mode);
}
//獲取測量模式
@MeasureSpecMode
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
//獲取測量大小
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
可以看到,MeasureSpec類還是挺簡單的,MeasureSpec類通過將mode和size打包成一個32位int值來減少了對象內(nèi)存分配,并提供了打包和解包的方法。
3.4 確定MeasureSpec值
在measure過程中,系統(tǒng)會將View的LayoutParams和父容器所施加的規(guī)則轉(zhuǎn)換成對應的MeasureSpec,然后在onMeasure()方法中根據(jù)這個MeasureSpec來確定View的測量寬高。父容器所施加的規(guī)則對于DecorView與普通View是不同的,我們分開來看。
3.4.1 確定DecorView的MeasureSpec值
DecorView,作為頂級的View,我們平時setContentView()所設(shè)置的布局可能只是DecorView其中的一部分,如下圖所示:

關(guān)于
DecorView,可以看看我的另一篇文章:從setContentView揭開DecorView。
3.4.1.1 ViewRootImpl的PerformTraveals
在ViewRootImpl的PerformTraveals()方法中會獲得DecorView的MeasureSpec值:
private void performTraversals() {
//...
//獲得view寬高的測量規(guī)格,mWidth和mHeight表示窗口的寬高,lp.widthhe和lp.height表示DecorView根布局寬和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//執(zhí)行測量
//...
}
3.4.1.2 ViewRootImpl的getRootMeasureSpec
我們再來看看getRootMeasureSpec()方法:
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
//如果View的布局參數(shù)為MATCH_PARENT,則其為精確模式,大小為窗口的大小
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
//如果View的布局參數(shù)為WRAP_CONTENT,則其為最多模式,大小不定,但不能超過窗口的大小
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
//如果View的布局參數(shù)為準確的數(shù)值,如100dp等,則其為精確模式,大小為View設(shè)定的數(shù)值
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
可以看到:DecorView的MeasureSpec值是由窗口大小及其布局參數(shù)來決定的。
來張表格總結(jié):
| 布局參數(shù) | LayoutParams.MATCH_PARENT | LayoutParams.WRAP_CONTENT | 準確數(shù)值 |
|---|---|---|---|
| MeasureSpec值 | EXACTLY windowSize(窗口大?。?/td> | AT_MOST windowSize(不能超過窗口大?。?/td> | EXACTLY rootDimension(準確數(shù)值) |
3.4.2 確定普通View的MeasureSpec值
普通View的MeasureSpec值通過getChildMeasureSpec()方法來獲取,getChildMeasureSpec()位于ViewGroup中:
3.4.2.1 ViewGroup的getChildMeasureSpec
/**
*
* @param spec 父容器的測量規(guī)格(MeasureSpec)
* @param padding 子view的內(nèi)邊距和外邊距(padding,margin)
* @param childDimension 子view的布局參數(shù)(寬/高)
* @return 子view的測量規(guī)格(MeasureSpec)
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//父容器的測量模式
int specMode = MeasureSpec.getMode(spec);
//父容器的測量大小
int specSize = MeasureSpec.getSize(spec);
//子view大小 = 父容器大小-子view邊距(子view只在某些地方地方用到這個值,具體看下面的代碼)
int size = Math.max(0, specSize - padding);
//初始化子view的大小和模式(最終的結(jié)果需要下面代碼計算出來)
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
case MeasureSpec.EXACTLY://當父容器模式為EXACTLY時
if (childDimension >= 0) {// 當子view的LayoutParams>0,即有確切的值
//子view大小為子自身所賦的值,模式大小為EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {// 當子view的LayoutParams為MATCH_PARENT時(-1)
//子view大小為父容器剩余大小,模式為EXACTLY
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {// 當子view的LayoutParams為WRAP_CONTENT時(-2)
//子view決定自己的大小,但最大不能超過父容器剩余大小,模式為AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST: // 當父容器的模式為AT_MOST時
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:// 當父容器的模式為UNSPECIFIED時
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//這里的sUseZeroUnspecifiedMeasureSpec值為targetSdkVersion < Build.VERSION_CODES.M;即<23為true,否則為false.
//大于等于23時會將size作為提示值,否則為0
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
可以看到:普通View的MeasureSpec值是由父容器的MeasureSpec及其布局參數(shù)來決定的。
來張表格總結(jié):
| 父容器測量模式(右側(cè)) ---------------------------------- 子View布局參數(shù)(下側(cè)) |
EXACTLY(精確) | AT_MOST(最多) | UNSPECIFIED(未指定) |
|---|---|---|---|
| 具體dp/px | EXACTLY childDimension(子View尺寸) |
EXACTLY childDimension(子View尺寸) |
EXACTLY childDimension(子View尺寸) |
| MATCH_PARENT | EXACTLY parentSize(父容器剩余大?。?/td> | AT_MOST parentSize(不能超過父容器剩余大?。?/td> | UNSPECIFIED targetSdkVersion<23 ? 0 : size; (大于等于23時會將size作為提示值) |
| WRAP_CONTENT | AT_MOST parentSize(不能超過父容器剩余大?。?/td> | AT_MOST parentSize(不能超過父容器剩余大小)) |
UNSPECIFIED targetSdkVersion<23 ? 0 : size; (大于等于23時會將size作為提示值) |
對上表作一些規(guī)律總結(jié):
- 以子View布局參數(shù)為標準,橫向觀察:
- 當子View使用具體的dp/px時,無論父容器的測量模式是什么,子View的測量模式都是EXACTLY且大小等于設(shè)置的具體數(shù)值;
- 當子View使用match_parent時,子View的測量模式與父容器的測量模式保存一致。
另外,UNSPECIFIED模式一般很少用到,可以不用過多關(guān)注。
4. measure過程分析
measure過程根據(jù)View的類型可以分為兩種情況:
- 測量單一
View時,只需測量自身; - 測量
ViewGroup時,需要對ViewGroup中包含的所有子View都進行測量。
我們對這兩種情況分別進行分析。
4.1 單一View的measure過程
單一View的measure過程由View的measure()來實現(xiàn):
4.1.1 View的measure
/**
*
* @param widthMeasureSpec view的寬測量規(guī)格
* @param heightMeasureSpec view的高測量規(guī)格
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//...
onMeasure(widthMeasureSpec, heightMeasureSpec);
//...
}
這里傳入的widthMeasureSpec/heightMeasureSpec是當前View的測量規(guī)格,通過getChildMeasureSpec()來獲得的。具體細節(jié)可以看上面的分析。
measure()方法為final類型,子類不能對其進行重寫。measure()方法中主要就是對基本測量邏輯作判斷,最終會調(diào)用onMeasure()方法。
4.1.2 View的onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
上面就一行代碼,看起來很簡單,但是包含了三個方法:getSuggestedMinimumWidth()/getSuggestedMinimumHeight()、getDefaultSize()、setMeasuredDimension(),我們分開來看:
4.1.3 View的getSuggestedMinimumWidth
getSuggestedMinimumHeight()與getSuggestedMinimumWidth()同理,我們這里只看下getSuggestedMinimumWidth():
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
如果View沒有設(shè)置背景,那么View的寬度為mMinWidth,而mMinWidth就是android:minWidth屬性所指定的值,若android:minWidth沒有指定,則默認為0;
如果View設(shè)置了背景,那么View的寬度為mMinWidth和mBackground.getMinimumWidth()中的最大值。
那么mBackground.getMinimumWidth()的值是多少呢,我們來看看:
4.1.4 Drawable的getMinimumWidth
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();//返回背景圖Drawable的原始寬度
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}
可以看出:mBackground.getMinimumWidth()返回的是背景圖Drawable的原始寬度,若無原始寬度則返回0。
那么Drawable什么情況下有原始寬度?如:ShapeDrawable沒有,但BitmapDrawable有。
4.1.5 View的getDefaultSize
我們再來看看View的getDefaultSize()方法:
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);//獲取測量模式
int specSize = MeasureSpec.getSize(measureSpec);//獲取測量大小
switch (specMode) {
//測量模式為UNSPECIFIED時,View的測量大小為默認大小,即getSuggestedMinimumWidth()/getSuggestedMinimumHeight()返回的結(jié)果。
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//測量模式為AT_MOST、EXACTLY時,View的測量大小為測量規(guī)格中的測量大小
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
4.1.6 View的setMeasuredDimension
再來看setMeasuredDimension():
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
//...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
setMeasuredDimension()里會調(diào)用setMeasuredDimensionRaw():
4.1.7 View的setMeasuredDimensionRaw
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
setMeasuredDimensionRaw()中就是對測量出的寬高進行保存。
到這里,單一View的measure過程就完成了。
4.1.8 時序圖
最后,來張相關(guān)的時序圖:

4.1.9 流程圖
以及來張測量過程細節(jié)流程圖:

4.2 ViewGroup的measure過程
由于ViewGroup包含了子View,因此其measure過程是通過遍歷所有的子View并對子View測量,然后合并所有子View的尺寸,最后得到ViewGroup的測量值。

如上圖所示,要執(zhí)行ViewGroup的measure,首先從頂部的ViewGroup開始遍歷,自上而下遞歸執(zhí)行子View的measure。
ViewGroup的measure過程是從measureChildren()開始的。
4.2.1 ViewGroup的measureChildren
/**
*
* @param widthMeasureSpec ViewGroup的寬測量規(guī)格
* @param heightMeasureSpec ViewGroup的高測量規(guī)格
*/
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {//遍歷子View
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
再來看measureChild():
4.2.2 ViewGroup的measureChild
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();//獲得子view的布局參數(shù)
//獲得子view的寬/高測量規(guī)格
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
//子view開始測量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
getChildMeasureSpec()的代碼請看上面的分析。
4.2.3 View的measure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//...
onMeasure(widthMeasureSpec, heightMeasureSpec);
//...
}
這里的measure跟上面單一View的measure方法是一樣的。
4.2.4 ViewGroup的onMeasure
如果子View是一個單一的View的話,則直接調(diào)用View的onMeasure()方法,跟上面單一View的measure過程是一樣大的;
如果子View是ViewGroup的話,理論上應該是應該調(diào)用ViewGroup的onMeasure()方法的。
但實際上,ViewGroup中是沒有onMeasure()這個方法的,ViewGroup作為一個抽象類,需要其子類去實現(xiàn)測量過程的onMeasure()方法,比如LinearLayout、RelativeLayout等。因為LinearLayout、RelativeLayout這些布局具體不同的特性,其測量細節(jié)各有不同,無法進行統(tǒng)一的實現(xiàn),因此需要其子類去進行具體的實現(xiàn)。因此我們自定義ViewGroup時需要重寫onMeasure()方法。
我們這里看下LinearLayout的onMeasure()實現(xiàn):
4.2.5 LinearLayout的onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {//方向判斷
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
LinearLayout會區(qū)分方向來進行不同的測量方法,我們主要看下豎向的測量measureVertical(),橫向的原理差不多這里就不看了。
4.2.6 LinearLayout的measureVertical
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
//獲取子view數(shù)量
final int count = getVirtualChildCount();
//獲取LinearLayout的寬/高測量模式
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//...
for (int i = 0; i < count; ++i) {//遍歷子View
final View child = getVirtualChildAt(i);
//..
// 最終會調(diào)用子View的measure(),計算出子View的大小
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
// 獲取子View的測量高度
final int childHeight = child.getMeasuredHeight();
//..
final int totalLength = mTotalLength;
// 將子View的測量高度以及Margin加到LinearLayout的總高度上
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
}
//..
// 加上LinearLayout設(shè)置的Padding
mTotalLength += mPaddingTop + mPaddingBottom;
int heightSize = mTotalLength;
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
//..
//保存測量值
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),heightSizeAndState);
}
最后會調(diào)用setMeasuredDimension()來保存測量好的值,至此ViewGroup的測量過程就完成了。
4.2.7 時序圖
最后,來張相關(guān)的時序圖:

4.2.8 流程圖
以及來張ViewGroup測量過程細節(jié)流程圖:

5. 自定義View
5.1 自定義單一View
自定義單一View,可以直接使用View中默認定義的onMeasure()方法。如果有時需要更精準的測量,可以重寫onMeasure()。
5.2 自定義ViewGroup
由于ViewGroup沒實現(xiàn)onMeasure(),所以自定義ViewGroup需要重寫onMeasure()方法。這里給個簡單的模板:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//定義存放測量后的View的寬和高的變量
int widthMeasure;
int heightMeasure;
//實現(xiàn)測量方法以及具體的測量細節(jié)
measureMethod();
//必不可少 保存測量后View寬和高的值
setMeasuredDimension(widthMeasure, heightMeasure);
}
6. 其他問題
6.1 獲取View的測量寬高
我們可以使用getMeasuredWidth()和getMeasuredHeight()來獲取View測量出的寬高。
但是從上面的代碼分析可以看到,在調(diào)用setMeasuredDimension()方法之后,我們才能使用getMeasuredWidth()和getMeasuredHeight()來獲取View測量出的寬高,在這之前去調(diào)用這兩個方法得到的值都會是0。
結(jié)合Activity的啟動過程以及生命周期,在onCreate()和onResume()中,都還未開始進行測量的操作,所以這時候調(diào)用getMeasuredWidth()和getMeasuredHeight()的值都會是0。開始測量的相關(guān)代碼最早可以追涉到ActivityThread的handleResumeActivity()方法,可以看下這篇文章的介紹:Activity啟動過程詳解。
另外,在某些情況下,需要多次測量才能確定View最終寬高;因此這種情況下獲得的值可能是不準確的,建議在layout過程中onLayout()去獲取最終寬高。
6.2 自定義View時WRAP_CONTENT不生效的問題
從上面getDefaultSize()方法的代碼可以看到,無論是AT_MOST模式還是EXACTLY模式,View的測量大小都為測量規(guī)格中的測量大小,所以我們就會看到自定義View使用WRAP_CONTENT會起到跟MATCH_PARENT的效果。
為了解決這個問題需要在LayoutParams屬性為WRAP_CONTENT時指定一下默認的寬和高,如:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width=100;//設(shè)置一個默認寬
int height=100;//設(shè)置一個默認高
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(width, height);//寬高都為WRAP_CONTENT都設(shè)置為默認寬高
} else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(width, heightSize);//只有寬為WRAP_CONTENT時,只設(shè)置默認寬
} else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, height);//只有高為WRAP_CONTENT時,只設(shè)置默認高
}
}
這里的默認值請根據(jù)實際情況去確認。
像系統(tǒng)的TextView這些都支持WRAP_CONTENT,可以去看下其onMeasure()的源碼實現(xiàn)。
如果我們自定義的View只需要設(shè)置指定的具體寬高或者MATCH_PARENT,可以不用重寫onMeasure()方法。