一、默認處理
View類默認實現:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
默認實現中,調用了setMeasuredDimension( )設置測量后的寬高,這個方法需要兩個參數,也就是我們測量后的寬、高信息.
二、widthMeasureSpec、heightMeasureSpec
1、寬、高信息
不管是重寫onMeasure還是默認的實現中,我們都可以看到兩個int類型值:widthMeasureSpec、heightMeasureSpec.
這兩個int類型值就是寬和高信息,注意:無論寬度信息(widthMeasureSpec)還是高度信息(widthMeasureSpec),都包含兩種數據------模式、大小,模式是根據wrap_content、match_parent、具體值來確定的,大小則是根據不同模式進行計算.
2、模式
我們在xml中定義layout_width、layout_height屬性時有3種情況:
wrap_content
match_parent
具體值
寬、高信息中的模式也有三個不同的常量值:
MeasureSpec.EXACTLY
當組件的尺寸為match_parent或具體值時用該常量值表示此時的模式
MeasureSpec.AT_MOST
當組件的尺寸為wrap_content時用該常量值表示此時的模式
MeasureSpec.UNSPECIFIED
未指定尺寸,一般用不到
很好理解,EXACTLY譯為確定的,使用match_parent表示組件大小匹配父容器,而父容器本身也是一個組件,它會計算出自己的大小,我們不需要重復去計算,所以無論是match_parent還是具體值,組件大小都是確定的,所以這兩種情況都用MeasureSpec.EXACTLY來表示.
使用wrap_content(包裹內容)時,此時組件的大小需要根據內容確定,只要在父容器指定的最大尺寸之內就行了,用MeasureSpec.AT_MOST來表示.
而MeasureSpec.UNSPECIFIED表示父容器對子視圖不進行任何約束,子視圖可以是它想要的任何大小,一般用不到.
3、獲取模式、大小
widthMeasureSpec、heightMeasureSpec都是一個int類型值,那么一個int
怎么保存模式、大小這兩種數據呢?
我們都知道:int類型占用4個字節,一共32位. 而widthMeasureSpec和heightMeasureSpec的前2位代表模式,后30位表示大小.
ok,既然知道怎么表示的,那么我們可以通過位運算來獲取模式和大?。?/p>
模式 int mode = widthMeasureSpec & 0x3 << 30;
大小 int size = widthMeasureSpec & ~0x3 << 30;
每次都要進行位運算很麻煩,所以系統提供了MeasureSpec類,這個類有3個常量(就是上面的三種模式)以及一些靜態方法:
三種模式:
MeasureSpec.EXACTLY
MeasureSpec.AT_MOST
MeasureSpec.UNSPECIFIED
方法:
public static int makeMeasureSpec(int size, int mode)
傳入尺寸大小和模式生成一個包含這兩個信息的int類型值
從一個包含模式、大小的int類型值中獲取模式、大小
public static int getMode(int measureSpec)
public static int getSize(int measureSpec)
三、正方形View
public class XView extends View {
public XView(Context context) {
this(context, null);
}
public XView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = myMeasure(0, widthMeasureSpec);
int height = myMeasure(0, heightMeasureSpec);
// 和系統onMeasure() 方法不同的是:我添加了下面的if-else語句
if (width > height) {
width = height;
} else {
height = width;
}
setMeasuredDimension(width, height);
}
/**
*雖然寫著方法名是myMeasure,但是其實這就是View類getDefaultSize()方法的源碼
*注意看文章開頭,setMeasuredDimension()需要的兩個參數:寬、高信息都是通過這個方法生成的
*/
private int myMeasure(int defaultValue, int measureSpec) {
int result = defaultValue;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = defaultValue;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
}
上面定義了一個正方形的XView,不論XView在布局文件中的寬、高如何定義,顯示在界面上始終是一個正方形的View(你需要設置一個背景顏色,不然看不出效果),感興趣的可以運行查看效果.