自定義View_onDraw,onMeasure方法

package xena.view;

import xena.act.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class MyView extends View {
	private Context context;
	private int r = 10;
	private String str = "小";
	private final String NS = "http://www.hqyj.com";// 名稱空間
	private boolean flag;

	public float getR() {
		return r;
	}

	public void setR(int r) {
		this.r = r;
		this.invalidate();//觸發onDraw方法的執行
	}

	public String getStr() {
		return str;
	}

	public void setStr(String str) {
		this.str = str;
		this.invalidate();//觸發onDraw方法的執行
	}

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
		this.invalidate();//觸發onDraw方法的執行
	}

	// 用於自定義View當作標籤時用的
	public MyView(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.context = context;
		// 參數1:名稱空間, 參數2:自定義屬性名, 取失敗時當作默認值返回
		this.r = attrs.getAttributeIntValue(NS, "r", 20);
	//	this.flag = attrs.getAttributeBooleanValue(NS, "flag", false);
		// 參數1:我稱空間, 參數2: 自定義屬性名
		// this.str = attrs.getAttributeValue(NS, "text");
		// 如果屬性值是資源文件id
		// int getAttributeResourceValue(String namespace, String attributeName,
		// int defaultValue);
		
		int id = attrs.getAttributeResourceValue(NS, "text", -1);
		if (id != -1) {
			// int id = R.string.hello_world;
			Resources resources = this.context.getResources();
			this.str = resources.getString(id);
		}else {
			this.str = attrs.getAttributeValue(NS, "text");
		}
	}

	// 用於new MyView(...)用的
	public MyView(Context context) {
		super(context);
	}
	private int getwidth(int widthMeasureSpec) {
		int width = 10;
		int width_mode = MeasureSpec.getMode(widthMeasureSpec);
		if(width_mode==MeasureSpec.EXACTLY) {
			width = MeasureSpec.getSize(widthMeasureSpec);
		}else if(width_mode==MeasureSpec.AT_MOST) {
			width = (int) (2*r);
		}else if(width_mode==MeasureSpec.UNSPECIFIED) {
			width = 100;
		}
		return width;
	}
	// 00  11  10  11
	
	private int getheight(int heightMeasureSpec) {
		int hight = 10;
		int hight_mode = MeasureSpec.getMode(heightMeasureSpec);
		if(hight_mode==MeasureSpec.EXACTLY) {
			hight = MeasureSpec.getSize(heightMeasureSpec);
		}else if(hight_mode==MeasureSpec.AT_MOST) {
			hight= (int) (2*r);
		}else if(hight_mode==MeasureSpec.UNSPECIFIED) {
			hight = 100;
		}
		return hight;
	}
	//在onDraw前調用
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int width=0, hight=0;
		width = this.getwidth(widthMeasureSpec);
		hight = this.getheight(heightMeasureSpec);
		this.setMeasuredDimension(width, hight);
	}
//	// 用於繪製界面上的內容,當界面顯示時調用
	@Override
	protected void onDraw(Canvas canvas) {// canvas畫布對象
		super.onDraw(canvas);
		// 創建筆 畫
		Paint paint = new Paint();
		paint.setColor(Color.BLUE);
		// 畫圓
		canvas.drawCircle(r, r, r, paint);
		paint.setColor(Color.RED);

		if (flag) {
			canvas.drawCircle(r, r, 5, paint);
		}
		// 畫字
		// 參數1:被繪製的字符串, 參數2,3:指字符串每一個字符的左下角座標
		paint.setTextSize(30);// 設置文字大小
		canvas.drawText(this.str, r, r, paint);
	}
}

(1)一般情況重寫onMeasure()方法作用是爲了自定義View尺寸的規則,如果你的自定義View的尺寸是根據父控件行爲一致,就不需要重寫onMeasure()方法 

(2)如果不重寫onMeasure方法,那麼自定義view的尺寸默認就和父控件一樣大小,當然也可以在佈局文件裏面寫死寬高,而重寫該方法可以根據自己的需求設置自定義view大小
(3)widthMeasureSpec和heightMeasureSpec這兩個值通常情況下都是由父視圖經過計算後傳遞給子視圖的,說明父視圖會在一定程度上決定子視圖的大小。

1.

wrap--AT_MOST

fill--EXACTLY

固定值--EXACTLY

上邊是大家都知道的,但是一直很疑惑UNSPECIFIED什麼情況發生,今天測了下,發現在使用weigt時會調用,因爲此時定義的值爲0dp,這不就是UNSPECIFIED嘛!

父       match_parent     match_parent            match_parent            wrap_content      wrap_content        wrap_content       200px                  200px                    200px    
子       match_parent     100px                        wrap_content             match_parent     100px                    wrap_content       100px                  match_parent        wrap_content
結果    exactly(fill)          exactly(100px)          at_most(fill)            at_most    (fill)     exactly(100xp)      at_most(fill)           exactly(100px)    exactly(200px)       at_most(200xp)

1.有at_most  適合自定義一個大小,(子的大小超不過父大小)
2.有exactly 則適合測出的大小. 
3,有unspecified, 適合任意指定大小,(子的大小可超過父大小)
 可以說重載onMeasure(),onLayout(),onDraw()三個函數構建了自定義View的外觀形象。再加上onTouchEvent()等重載視圖的行爲,可以構建任何我們需要的可感知到的自定義View。

不管是自定義View還是系統提供的TextView這些,它們都必須放置在LinearLayout等一些ViewGroup中,因此理論上我們可以很好的理解onMeasure(),onLayout(),onDraw()這三個函數:1.View本身大小多少,這由onMeasure()決定;2.View在ViewGroup中的位置如何,這由onLayout()決定;3.繪製View,onDraw()定義瞭如何繪製這個View。

widthMeasureSpec, heightMeasureSpec這兩個參數是從哪裏來的?onMeasure()函數由包含這個View的具體的ViewGroup調用,因此值也是從這個ViewGroup中傳入的。這裏我直接給出答案:子類View的這兩個參數,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同決定。權值weight也是尤其需要考慮的因素,有它的存在情況可能會稍微複雜點。

這個值由高32位組成,最高的兩位保存的值叫specMode,可以通過如代碼中所示的MeasureSpec.getMode()獲取;低30位爲specSize,同樣可以由MeasureSpec.getSize()獲取

所有的View的onMeasure()的最後一行都會調用setMeasureDimension()函數的作用——這個函數調用中傳進去的值是View最終的視圖大小。也就是說onMeasure()中之前所作的所有工作都是爲了最後這一句話服務的。

specMode一共有三種可能:

MeasureSpec.EXACTLY:父視圖希望子視圖的大小應該是specSize中指定的。

MeasureSpec.AT_MOST:子視圖的大小最多是specSize中指定的值,也就是說不建議子視圖的大小超過specSize中給定的值。

MeasureSpec.UNSPECIFIED:我們可以隨意指定視圖的大小。


MATCH_PARENT對應於EXACTLY,WRAP_CONTENT對應於AT_MOST,其他情況也對應於EXACTLY,它和MATCH_PARENT的區別在於size值不一樣。




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