-
Android中每一個組件的繪製過程,都要經過三個階段:測量、佈局、繪製,分別對應着方法
onMeasure
、onLayout
、onDraw
(這三個方法定義於View類中)。當然,這三個方法都是允許組件自己重定義的方法,來實現組件對自己的尺寸進行測量、對自己進行佈局以及繪製自己的內容。
onMeasure
有child views時,要分別對子組件調用相關測量方法,比如
measureChild
、measureChildWithMargins
等,並根據子組件的dimension來確定自己的尺寸;最後要調用
setMeasuredDimension(measuredWidth, measuredHeight)
方法來保存自己的尺寸信息。onLayout
對於container,需要對子組件進行佈局,調用子組件的
layout(int l, int t, int r, int b)
方法,傳入相對於container的座標。最終同樣會執行到子組件的onLayout
方法來實現對子組件的佈局。佈局說白了就是確定自己的 繪製位置 與 大小,即左上右下四個座標 - 這樣也就提供了大小。
對於容器來說,將其中的具體組件佈局好了,也就將其自身佈局好了。
onDraw
使用該方法的參數Canvas進行內容繪製,其中的座標系是組件內座標系。
其實自定義動畫往往就是藉助屬性動畫來對繪製參數進行變化同時於onDraw時體現出變化效果來實現的。
-
從源碼中看,Android的繪圖是從ViewRootImpl類的
performTraversals
方法開始的,可以把這個方法視爲一個頂層的控制方法,在其中控制整個繪圖的流程。具體情況如下所述:
首先,在其中會調用
performMeasure
方法,在performMeasure
方法中調用View的measure
方法,進而調用到具體組件所實現的onMeasure
方法。View的
measure
是final方法,方法原型爲:public final void measure(int widthMeasureSpec, int heightMeasureSpec)
也就是說不允許子類修改測量的框架,只能夠修改真正進行測量工作的
onMeasure
方法。然後,測量結束會調用
performLayout
方法,在performLayout
方法中調用View的layout
方法,該方法原型爲:public void layout(int l, int t, int r, int b)
在該方法中會調用View的
onLayout
方法,對組件進行佈局。在拓展ViewGroup類的時候,對於所重寫的
onLayout
方法,一般最後一步就是分別調用組件各自的layout
方法來“Place the child.”。之後,會調用
performDraw
方法,通過performDraw -> draw -> drawSoftware
最終會調用View的draw(Canvas)
方法。在
draw
方法中會有六步操作,在第三步“draw the content”時會調用onDraw(Canvas)
方法,進行內容的繪製。 -
如果我們要拓展ViewGroup類實現一個佈局,就要在其中重寫
onMeasure
方法來對佈局中的組件進行測量,並在獲得其中所有組件的尺寸後計算得到佈局的尺寸,然後調用setMeasuredDimension
方法進行設置;之後還需要重寫onLayout
方法,在其中調用各個組件的layout
方法,傳入計算出的組件座標位置,實現對組件的佈局。至於繪製,則由具體的組件自己重寫
onDraw
方法進行實現,在ViewRootImpl類的performTraversals
邏輯中進行控制。 -
getMeasuredXXX & getXXX
在測量結束後(調用方法
setMeasuredDimension
後),就可以調用getMeasuredWidth
和getMeasuredHeight
來獲取視圖測量出的寬和高了,在這之前這兩個方法返回值均爲0。在佈局結束後,就可以調用方法
getWidth
和getHeight
來獲取視圖的寬高了。由於一般情況下,會根據測量的情況去佈局組件,所以這兩個方法的返回值是一樣的。
-
關於MeasureSpec
MeasureSpec是一個32位的int數,其中前2位用來表示模式,餘下30位用來表示size。
包括的模式有:
EXACTLY
表示父視圖希望子視圖的大小應該是由specSize的值來決定的,系統默認會按照這個規則來設置子視圖的大小,開發人員當然也可以按照自己的意願設置成任意的大小。
AT_MOST
表示子視圖最多隻能是specSize中指定的大小,開發人員應該儘可能小得去設置這個視圖,並且保證不會超過specSize。
系統默認會按照這個規則來設置子視圖的大小,開發人員當然也可以按照自己的意願設置成任意的大小。
UNSPECIFIED
表示開發人員可以將視圖按照自己的意願設置成任意的大小,沒有任何限制。這種情況比較少見,不太會用到。
-
看
performTraversals
代碼時會發現,比如進行佈局的時候,代碼中所調用的是performLayout
方法,在該方法中所執行的關鍵操作是host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
當時我想,這一個調用怎麼實現對所有組件的佈局的呢?還因此在代碼中找了好一會兒循環語句,現在我明白了:
這個host是個佈局對象,調用其layout
會進一步調用其onLayout
方法,在onLayout
方法中實現對所有子組件的遍歷佈局。同理,測量也是這樣的。
Android必知必會之繪圖機制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.