View的學習記錄(一)
View的學習記錄
本篇目的
探討View的繪製過程以及分析View在繪製過程中我們可以參與改造的地方,為自定義View做基礎
View的呼叫
View檢視樹
Activity中檢視的結構
在Activity中,通過setContentView()來設定一個layout佈局,在呼叫後,佈局內容才會顯示出來
DecorView作為View的根佈局來管理所有的View,將檢視呈現在了phoneWindow中,DecorView負責View的所有監聽事件,通過WindowManagerService來接收,通過Activity物件來回調onClickListener
通過設定requestWindowFeature(Window.Feature.NO_TITLE)來取消appBar
在程式碼中,setContentView()之後,ActivityManagerService會回撥onResume()方法,此時,會把DecorView新增到PhoneWindow中,完成介面繪製.
View的測量
onMeasure()
所有的View都自帶一個onMeasure()函式,引數是MeasureSpec類
在onMeasure()方法中,最後,都是通過setMeasuredDimension()方法來儲存測量的結果
下面是ViewGroup的測量原始碼
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec) ,
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
我們可以通過在onMeasure()方法的最後,手動呼叫setMeasuredDimension()來儲存我們自己測量的結果
MeasurSpec類
MeasurSpec是一個設計短小精悍的類
自身是一個32位int值
高2位位測量模式,可以MeasureSpec.getMode()獲取
低30位為測量的大小,在計算中,使用的是位計算,為了提高計算速率,可以通過MeasureSpec.getSize()獲取
測量模式分三類
- EXACTLY
精確測量模式,對應我們指定控制元件大小,例如"100dp"或者match_parent這兩種情況 - AT_MOST
最大值模式,對應指定控制元件大小為wrap_content時,隨控制元件內容的變化而變化
還要不超過父控制元件允許的最大尺寸即可. - UNSPECIFIED
自定義模式
不會指定大小測量模式,View想多大就多大,通常製作自定義View時使用
View類預設的onMeasure()方法只支援EXACTLY模式,自定義View繼承自View時,就需要重寫onMeasure()方法,拓展對wrap_content模式的支援(否則,預設match_parent)
重寫onMeasure()
int MaxSize=200;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
private int measureHeight(int heightMeasureSpec) {
int result=0;
int specMode=MeasureSpec.getMode(heightMeasureSpec);
int specSize=MeasureSpec.getSize(heightMeasureSpec);
if (specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=MaxSize;
if (specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int result=0;
int specMode=MeasureSpec.getMode(widthMeasureSpec);
int specSize=MeasureSpec.getSize(widthMeasureSpec);
if (specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=MaxSize;
if (specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
View的繪製
測量好View之後,會呼叫onDraw()方法來繪製View.我們可以重寫onDraw()方法,來實現我們想要額外實現的效果
這裡就需要了解下系統2D繪圖API
在onDraw()方法中,會提供一個Canvas物件,控制元件就繪製在Canvas上.
畫布的建立是通過Bitmap物件,
Canvas canvas=new Canvas(bitmap);
bitmap用來承載canvas上所有的畫素資訊.
在onDraw(canvas)中,在canva上繪製背景.然後通過Canvas.drawBitmap()來繪製兩張畫布,在第二張畫布上來繪製我們的View,通過改變bitmap來引起View重繪,從而顯示改變後的Vbitmap
canVas相當於畫布,繪製就需要畫筆,系統提供給我們的畫筆是Paint及其子類TextPaint等
ViewGrouop的測量和繪製
測量
ViewGroup會管理其子View
當ViewGroup的模式為wrap_content時,就需要遍歷所有子View,呼叫子View自身的Measure()方法後,獲得所有子View的大小,來決定自己的大小,其他模式時,會通過具體的指定值來設定自身大小
放置
測量後,需要把子view放置在合適的位置上,就是View的layout過程,同樣是遍歷子View,呼叫子View的layout方法,來指定子View的具體位置
繪製
ViewGroup通常情況下不需要繪製,如果背景指定了顏色或者圖層,才會觸發ViewGroup自身的onDraw()方法.
一般情況下,ViewGroup會呼叫dispatchDraw()方法,來繪製子View,同樣是遍歷所有子View,呼叫子View的Draw()方法.
資料來自於<Android群英傳>