View的measure過程
View的measure過程由其measure方法來完成嫂伞,measure方法是一個(gè)final類型的方法辩昆,子類不能重寫此方法。在View的measure方法中會(huì)去調(diào)用View的onMeasure方法。
View#onMeasure:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasureDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
View#getDefaultSize:
public static int getDefaultSize(int size, int measureSpec) {
int result = size; //size大小由getSuggestedMinimumWidth方法或getSuggestedMinimumHeight方法確定
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;
}
- 當(dāng)前View的SpecMode為AT_MOST跟EXACTLY時(shí)偎血,getDefaultSize方法返回的大小就是measureSpec中的specSize霸株,而這個(gè)specSize就是View測(cè)量后的大小雕沉。
- 當(dāng)前View的SpecMode為UNSPECIFIED時(shí),getDefaultSize方法返回的大小就是getSuggestedMinimumWidth方法或getSuggestedMinimumHeight方法的返回值去件。
分析getSuggestedMinimumWidth方法:
View#getSuggestedMinimumWidth:
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
Drawable#getMinimumWidth:
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}
(1)如果View沒有設(shè)置背景坡椒,那么View的寬度為mMinWidth。
mMinWidth對(duì)應(yīng)于android:minWidth這個(gè)屬性所指定的值箫攀,如果不指定則默認(rèn)為0肠牲。
(2)如果View設(shè)置了背景,那么View的寬度為max(mMinWidth, mBackground.getMinimumWidth())靴跛。
Drawable#getMinimumWidth方法返回的是Drawable的原始寬度缀雳,前提是這個(gè)Drawable有原始寬度,否則就返回0梢睛。如ShapeDrawable無原始寬/高肥印,而BitmapDrawable有原始寬/高(圖片的尺寸)。
從getDefaultSize方法的實(shí)現(xiàn)來看绝葡,View的寬/高由SpecSize決定深碱,所有我們可以得出如下結(jié)論:直接繼承View的自定義控件需要重寫onMeasure方法并設(shè)置wrap_content時(shí)的自身大小,否則在布局中使用wrap_content就相當(dāng)于使用match_parent藏畅。
代碼如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasureDimension(mWidth, mHeight);
} elss if(widthSpecMode == MeasureSpec.AT_MOST) {
setMeasureDimension(mWidth, heightSpecSize );
} elss if(heightSpecMode == MeasureSpec.AT_MOST) {
setMeasureDimension(widthSpecSize , mHeight);
}
}
mWidth敷硅、mHeight是給View指定的默認(rèn)的內(nèi)部寬/高,并在wrap_content時(shí)設(shè)置此寬/高愉阎。對(duì)于非wrap_content情形绞蹦,沿用系統(tǒng)的測(cè)量值。
這個(gè)默認(rèn)的內(nèi)部寬/高可以根據(jù)需要靈活指定榜旦,例如TextView幽七、ImageView等控件針對(duì)wrap_content情形在onMeasure方法均做了特殊處理。