Android控件架構
在Activity中使用`setContentView(R.layout.activity_main)`來設置一個佈局。
每個Activity都包含一個Window對象,在Android中Window對象通常由PhoneWindow來實現。PhoneWindow將一個DecorView設置整個應用窗口的根View,這裏面所有的View監聽事件,都由WindowMannagerService來進行接收,並通過Activity對象來回調相應的onClickListener。
DecorView將屏幕分爲兩部分,一個是TitleView,一個是ContentView。ContentView是一個ID爲content的Framelayout,activity_main.xml就是設置在這樣一個Framelayout中。
注意:當程序在onCreate()方法中調用setContentView(R.layout.activity_main)
的時候,ActivityMannagerService會回調onResume(),此時系統纔會把整個DecorView添加到PhoneWindow中。
View的測量
在測量的時候使用到一個類MeasureSpec,代表一個32位的int值,其中高2位代表測量的模式,低30位代表測量的大小。
測量模式一般是分三種:
AT_MOST:當控件的寬和高屬性給定wrap_content的時候,使用的是該模式。
EXACTLY:當控件的寬和高給定一個具體值的時候,或者指定爲match_parent的時候,系統使用的是該模式。
UNSPECIFIED:父容器不對view有任何限制,要多大就給多大,這種情況一般用於系統內部,表示一種測量的狀態。
自定義View重寫onMeasure()
的時候,需要setMeasuredDimension()
來設置控件的大小。例如:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
private int measureHeight(int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int result = 0;
switch (heightMode){
case MeasureSpec.AT_MOST:
Log.i(TAG, "measureHeight: "+heightMode);
result = 200;
result = Math.min(heightSize, result);
break;
case MeasureSpec.EXACTLY:
Log.i(TAG, "measureHeight: "+heightMode);
result = heightSize;
break;
}
return result;
}
注意:如果沒有重寫onMeasure()
方法,那麼指定控件寬高爲wrap_content無效,系統不知道應該使用多大的尺寸,將默認充滿整個屏幕。
View的繪製
創建一個Canvas的時候,需要傳入一個bitmap:
Canvas canvas = new Canvas(bitmap);//裝載畫布
這個bitmap用來存儲所有繪製在canvas上的像素信息,canvas的drawXXX方法將會作用在這個bitmap上。
重寫onDraw方法,使用canvas的繪製API,但是其實並沒有將圖形繪製在onDraw()方法指定的那塊畫布上,
而是通過改變bitmap,然後讓View重繪,顯示改變之後的bitmap。
ViewGroup的測量與繪製
測量階段:當ViewGroup的大小爲wrap_content,ViewGroup首先會對子view進行遍歷,獲得子view的大小,然後決定自己的大小。而在其他模式下,則會通過具體的數值來設置自身的大小。
繪製階段:如果沒有指定ViewGroup的背景,那麼onDraw()不會被調用;但是,ViewGroup會使用dispatchDraw()方法來繪製其子view,其過程同樣是通過遍歷所有子view,並調用子view的繪製方法來完成繪製工作。
自定義View
自定義View:
1:重寫onDraw()來繪製View的顯示內容。
2:如果該view使用 wrap_content , 還需要重寫 onMeasure() 。
3:通過自定義attrs屬性,可以設置新的屬性配置值。使用 TypedArray 獲取完屬性的值後,要調用 TypedArray.recycle()
。
三種方式實現自定義的控件
1:對現有控件進行拓展。
2:通過組合來實現新的控件。(下個鏈接是一個topbar的小案例)
3:重寫view來實現全新的控件。
(1) 弧形展示圖
(2) 音頻條形圖
自定義ViewGroup
自定義ViewGroup:
1:重寫onMeasure()
對子View進行測量。
2:重寫onLayout()
來確定子view的位置。
3:重寫onTouchEvent()
方法增加響應事件。
類似Android原生控件 ScrollView 的自定義 ViewGroup :