/**
* 當在布局文件中聲明該view,由系統調用此函數創建對象
* @param context
* @param attrs
*/
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 當在代碼中用關鍵字 new 創建該view時,調用此方法
* @param context
*/
public MyView(Context context) {
super(context);
}2、布局文件中的使用 <zzmyviewz9.view.MyView
android:layout_width="wrap_content"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
/>3、重寫相關的方法,實現我們的需求:一個view 從創建對象,到顯示在屏幕上,中間幾個重要的步驟:1、測量大小 2、指定位置 3、繪制內容 /**
* 當系統需要測量當前控件大小時,
*/
PRotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 我們有個任務,就是指定我們自己的大小
// Measured 測量 Dimension 尺寸
setMeasuredDimension(180,100); // 指定我自己寬 180個象素 ,高100個象素
}
/**
* 二: 當系統為view指定位置時,調用此方法 ,對于自定義view 來說,該方法,作用不大
*/
protected void onLayout(boolean changed, int left, int top, int right,int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
/**
* 繪制內容
* @param Canvas 畫布
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLUE); // 繪制一個純蘭色
Paint paint = new Paint(); // 畫圓時,所用的畫筆
paint.setColor(Color.RED);
canvas.drawCircle(50, 50, 30, paint); // 繪制一個圓,圓心在x軸50象素,Y軸50像素的地方,半徑為30象素
} onMeasure(int,int); // 系統測量控件大小時調用該方法,自己給自己測量寬和高 onLayout(boolean,int,int,int,int);// 系統為該view 指定位置時調用此方法,子view的位置,自身只有建議權,決定權在父view的手中。傳遞的參數是距離父控件的左上右下的位置 onDraw(Canvas); // 為本view繪制內容時,調用該方法。 // 在主線程中請求重新繪制ondraw方法 invalidate(); // 在子線程中請求重新繪制ondraw方法 postInvalidate(); /*
* 自定義控件三部曲
* 1. 重寫onMeasure方法
* 用來自己給自己指定一個寬高 系統會給我們推薦一個樣式的寬高 但是具體寬高是多少是由我們自己說了算
* 2.重寫onLayout方法
這個方法 是當我們繼承自ViewGroup時復寫 當父控件決定了子控件距離它的左上點和右下點之后調用
* 3.
重寫onDraw方法 view長什么樣 是由自己決定
*/
// 覺得自己有多大
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
// 只要復寫了這個方法 最好自己給自定指定一個寬高
// 如果自己要給自己指定具體的寬高那么就 setMeasuredDimension 方法
// 如果想通過系統推薦 那么就調用父類的方法
// setMeasuredDimension(200, 100);
}
// 當父控件指定好自己的位置后 告訴我們位置在哪
// 在這里 這個方法的意思不是很大 可以不用理會
// 原因就是因為我們是繼承自View的
@Override
protected void onLayout(boolean changed, int left, int top, int right,int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
width = right-left;
height = bottom-top;
}
// 子控件決定自己長什么樣子
// 畫筆 畫布 畫布(一張白紙 由我們隨便作畫)
// 畫是一層一層的壓上去的
@Override
protected void onDraw(Canvas canvas) {
// 讓紙變成紅色
canvas.drawColor(Color.RED);
canvas.drawRect(20, 20, 80, 200, paint);
// 指定圓心和半徑
canvas.drawCircle(width/2, height/2, width/2, paint);
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
// 設置畫筆抗鋸齒
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
canvas.drawCircle(width/2, height/2, width/2*0.9f, paint);
}1、onMeasure和onLayout的方法詳解
測量的大?。菏莢iew 自己想要的大小,在 view.measure 方法執行完以后,就有了 view.getMeasuredWidth();// 測量的寬 view.getMeasuredHeight(); // 測量的高真實的寬和高: view 的真實大小, 是在 view.layout 方法執行完了以后,才會有的值 view.getWidth(); // 真實的寬 view.getHeight(); // 真實的高@Override
/**
* 當系統需要測量控件大小的時候,調用此方法,
* 如果是一個view 那么,指定自己的大小就可以了,
* 如果是一個布局(例如ViewGroup),不但要指定自己的大小,同時還要測量所有的子view 的大小。
* 參數一: widthMeasureSpec 寬度的大小和建議
* 參數一: heightMeasureSpec 高度的大小和建議
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 模式和size 模式是int類型的前兩位 size是后 30位
// int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); // 獲得寬度尺寸
// int modeWidth = MeasureSpec.getMode(widthMeasureSpec); // 獲得寬度的相應模式
/*
* 先測量誰(父控件還是子view),測量多少次都是不確定的
*/
//可以指定
// MeasureSpec.makeMeasureSpec(size, mode)
// 測量所有的子view的大小
for(int i=0;i<getChildCount();i++){
View view = getChildAt(i);
view.measure(widthMeasureSpec, heightMeasureSpec);
/*
* 測量的大小:是view 自己想要的大小,在 view.measure 方法執行完以后,就有了
* view.getMeasuredWidth();// 測量的寬
view.getMeasuredHeight(); // 測量的高
*/
}
//缺少這句話,則會導致沒有測量位置:導致linearlayout顯示,但是他的自view沒有顯示
//更好的方法是:使用for循環,給每一子view指定位置
// getChildAt(2).measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
/**
* 當父view為當前控件指定位置后,調用此方法 ,做為一個viewGroup,必須在此方法中,為子view指定位置
* @param changed 當前控件的大小,位置,是否發生改變
* @param l t r b 當前控件在父view坐標系中的位置
*
*/
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// System.out.println("ltrb:"+ l+" : "+t +" : "+r +" : "+b);
// 一個viewGroup在onlayout 中的任務就是為,子view 指定位置
// View child0 = getChildAt(0);
// child0.layout(0, 0, getWidth(), getHeight()); // 四個參數,分別是 child0 在當前view中的位置
// View child1 = getChildAt(1);
// child1.layout(getWidth(), 0, getWidth()*2, getHeight()); // 四個參數,分別是 child0 在當前view中的位置
for(int i=0;i<getChildCount();i++){
View view = getChildAt(i);
// 讓第一個子view填充滿整個viewGroup,以后的子view,依次向右移動一個寬度
view.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());
// view 的真實大小, 是在 view.layout 方法執行完了以后,才會有的值
// view.getWidth(); // 真實的寬
// view.getHeight(); // 真實的高
}
}注意:@Override
// 系統測量控件大小時,調用
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// getMeasuredHeight(); //
}
@Override
// 當父view為我們指定好位置后,調用此方法,告訴我們的位置
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
// 真實的高度,只有當 onLayout 執行了以后才能獲得
this.top = getHeight();
System.out.println(top);
}
新聞熱點
疑難解答