解決自定義控件大小問題

對於一些我們不能直接使用的控件,我們通常會重寫View來自定義功能和樣式,這時放在佈局文件裏,設置爲wrap_content後也是fill_parent的樣式,不能在一個頁面內同時顯示兩個自定義的組件,這時我們就需要重寫onMeasure()方法。具體重寫自定義組件的方法參見官方開發文檔:http://developer.android.com/guide/topics/ui/custom-components.html


一般來說,自定義控件都會去重寫View的onMeasure方法,因爲該方法指定該控件在屏幕上的大小。
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)


onMeasure傳入的兩個參數是由上一層控件傳入的大小,有多種情況,重寫該方法時需要對計算控件的實際大小,然後調用setMeasuredDimension(int, int)設置實際大小。
onMeasure傳入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸數值,而是將模式和尺寸組合在一起的數值。我們需要通過int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。


mode共有三種情況,取值分別爲MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精確尺寸,當我們將控件的layout_width或layout_height指定爲具體數值時如andorid:layout_width="50dip",或者爲FILL_PARENT是,都是控件大小已經確定的情況,都是精確尺寸。
MeasureSpec.AT_MOST是最大尺寸,當控件的layout_width或layout_height指定爲WRAP_CONTENT時,控件大小一般隨着控件的子空間或內容進行變化,此時控件尺寸只要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。


因此,在重寫onMeasure方法時要根據模式不同進行尺寸計算。下面代碼就是一種比較典型的方式:

// 例如重寫一個可滑動的SlipButton,bg_on爲“開”時的背景
Bitmap bg_on;
...
// 初始化等其他操作。
...

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
	// 這裏要計算一下控件的實際大小,然後調用setMeasuredDimension來設置
  int width = this.getMeasuredSize(widthMeasureSpec, true);
  int height = this.getMeasuredSize(heightMeasureSpec, false);
  setMeasuredDimension(width, height);
} 

/**
	* 計算控件的實際大小
	* @param length onMeasure方法的參數,widthMeasureSpec或者heightMeasureSpec
	* @param isWidth 是寬度還是高度
	* @return int 計算後的實際大小
	*/
private int getMeasuredSize(int length, boolean isWidth){
	// 模式
	int specMode = MeasureSpec.getMode(length);
	// 尺寸
	int specSize = MeasureSpec.getSize(length);
	// 計算所得的實際尺寸,要被返回
	int retSize = 0;        
	// 得到兩側的padding(留邊)
	int padding = (isWidth? getPaddingLeft()+getPaddingRight():getPaddingTop()+getPaddingBottom());
        
	// 對不同的指定模式進行判斷
	if(specMode==MeasureSpec.EXACTLY){  // 顯式指定大小,如40dp或fill_parent
		retSize = specSize;
	}else{                              // 如使用wrap_content
		retSize = (isWidth? bg_on.getWidth()+padding : bg_on.getHeight()+padding);
		if(specMode==MeasureSpec.UNSPECIFIED){
		retSize = Math.min(retSize, specSize);
		}
	}        

	return retSize;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章