【UI】【View】自定義佈局方法與注意事項(一)

本篇博客主要記錄自定義佈局的方法和注意事項。

(一直對自定義View感興趣,學習後怕忘記,特此總結記錄。學習View過程中,主要參考了鴻洋_大神的博客。)

【張鴻洋的博客】http://blog.csdn.net/lmj623565791/article/details/38339817 

         

一、自定義佈局需要實現的方法

1. 首先要重寫onMeasure()方法:onMeasure方法主要完成對此自定義佈局尺寸的測量。

2. 然後要重寫onLayout()方法:onLayout方法完成此自定義佈局中childView位置的指定。

3. 要定義一個內部類,返回LayoutParams,用於確定childView支持哪些屬性。


二、方法詳解

1. onMeasure():

:在定義佈局XML文件時,我們要對佈局控件定義兩個屬性。一個是android:layout_width,另一個是 android:layout_height。這些屬性值可以選擇match_parent、npx、或者wrap_content。

:父控件會傳遞給子控件一個MeasureSpec,可以獲得父控件對子控件寬高的測量模式和測量值。

       當子控件屬性是match_parent和npx時,測量模式是EXACTLY。

       當子控件屬性是wrap_content時,測量模式是AT_MOST。此時子控件的尺寸是由子控件的內容決定。寬高值不是父控件傳入        的測量值,而需要在自己的onMeasure()方法中確定。

在系統測量和繪製View時,主要將佈局解析成View樹。通過getChildAt(i)可以獲得對應的子控件。

       onMeasure()方法中必須調用setMeasuredDimension()方法設置本自定義控件尺寸。下面是onMeasure():


 

        //主要進行測量和確定CircleMenu的尺寸,判斷是根據父控件測量的值設置,還是根據子控件的大小設置尺寸
	//必須調用setMeasuredDimension()方法
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
		
		//獲得父控件傳遞給CircleMenu的測量值和測量模式
		int widthMode= MeasureSpec.getMode(widthMeasureSpec);
		int widthSize= MeasureSpec.getSize(widthMeasureSpec);
		int heightMode= MeasureSpec.getMode(heightMeasureSpec);
		int heightSize= MeasureSpec.getSize(heightMeasureSpec);
		Log.d(CRICLEMENU_TAG, widthMode+","+widthSize+","+heightMode+","+heightSize);
		
		//測量出所有子view的尺寸
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		//測量出的子view寬,高
		int cWidth;
		int cHeight;
		//佈局上面兩個view的寬度,下面兩個view的寬度,左邊兩個view的高度,右邊兩個view的高度
		int tWidth=0;
		int bWidth=0;
		int lHeight=0;
		int rHeight=0;
		//tWidth,bWidth中的最大寬度;lHeight,rHeight中的最大高度
		int maxWidth=0;
		int maxHeight=0;
		
		//若是 wrap_content,獲得CircleMenu的大小
		//獲得佈局中子view的數量
		int cCount= getChildCount();
		Log.d(CRICLEMENU_TAG, "cCount="+cCount);
		
		for(int i=0; i<cCount; i++){
			View childview= getChildAt(i);
			cWidth= childview.getMeasuredWidth();
			cHeight= childview.getMeasuredHeight();
			
			if(i==0){
				tWidth+=cWidth;
				lHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", tWidth="+tWidth+", lHeight="+lHeight);
			}
			
			if (i==1) {
				tWidth+=cWidth;
				rHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", tWidth="+tWidth+", rHeight="+rHeight);
			}
			
			if(i==2){
				bWidth+=cWidth;
				lHeight+=cHeight;	
				Log.d(CRICLEMENU_TAG, "i="+i+", bWidth="+bWidth+", lHeight="+lHeight);
			}
			
			if(i==3){
				bWidth+=cWidth;
				rHeight+=cHeight;
				Log.d(CRICLEMENU_TAG, "i="+i+", bWidth="+bWidth+", rHeight="+rHeight);
			}	
		}
		//獲取最大寬度,最大高度
		maxWidth= Math.max(tWidth, bWidth);
		maxHeight= Math.max(lHeight, rHeight);
		
		setMeasuredDimension((widthMode==MeasureSpec.EXACTLY)?widthSize:maxWidth, 
				(heightMode==MeasureSpec.EXACTLY)?heightSize:maxHeight);
		
		Log.d(CRICLEMENU_TAG, ((widthMode==MeasureSpec.EXACTLY)?widthSize:maxWidth)+","
		+((heightMode==MeasureSpec.EXACTLY)?heightSize:maxHeight));
		
	} //end測量佈局尺寸結束


2. onLayout():

此方法中必須調用childView.layout(cl,ct,cr,cb),其中四個參數是子控件相對父控件上下左右四個邊的距離。

//爲子view安排位置,必須調用layout()方法
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		//獲得子view的個數
		int cCount= getChildCount();
		//定義子view的長寬,和margin
		int mChildWidth=0;
		int mChildHeight=0;
		MarginLayoutParams mChildParams=null;
		
		
		//獲得子view的長寬
		for(int i=0;i<cCount;i++){
			View childview=getChildAt(i);
			mChildWidth=childview.getMeasuredWidth();
			mChildHeight=childview.getMeasuredHeight();
			mChildParams=(MarginLayoutParams) childview.getLayoutParams();
			
			//定義子view四條邊與父控件的距離
			int cl=0;
			int ct=0;
			int cr=0;
			int cb=0;
			
			switch (i) {
			case 0:
				cl=mChildParams.leftMargin;
				ct=mChildParams.topMargin;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				break;
				
			case 1:
				cl=mChildParams.leftMargin+mChildWidth;
				ct=mChildParams.topMargin;
				Log.v(CRICLEMENU_TAG, cl+","+ct);

			case 2:
				cl=mChildParams.leftMargin;
				ct=mChildParams.topMargin+mChildHeight;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				
			case 3:
				cl=getWidth()-mChildParams.rightMargin;
				ct=mChildParams.topMargin+mChildHeight;
				Log.v(CRICLEMENU_TAG, cl+","+ct);
				
			default:
				break;
			}
			
			cr=cl+mChildWidth;
			cb=ct+mChildHeight;
			//使用layout進行子view的佈局
			childview.layout(cl, ct, cr, cb);
			
			//打印出子view四個邊距離父控件的距離
			Log.d(CRICLEMENU_TAG, cl+","+ct+","+cr+","+cb);
		}
		
	}


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