View的繪制包括如下三個過動作:
動作 | 調(diào)用方法 | 功能 |
---|---|---|
Measure | onMeasure() | 測量得到該控件的長和寬 |
Layout | onLayout() | 指定子控件的擺放位置(ViewGroup必須實現(xiàn)) |
Draw | onDraw() | 進行實際繪制 |
注意:View的繪制過程在Activity的
onResume()
方法之后才進行,即onMeasure()
、onLayout()
、onDraw()
方法在onResume()
方法之后執(zhí)行
-
Measure --(onMeasure(int widthMeasureSpec,int heightMeasureSpec))
- View進行繪制前,首先會調(diào)用
Measure(int widthMeasureSpec,int heightMeasureSpec)
方法對控件的寬高進行測量確定,而Measure()
實際通過調(diào)用onMeasure(int widthMeasureSpec,int heightMeasureSpec)
方法完成測量,并將測量的寬高最終通過setMeasuredDimension(measuredWidth,measuredHeight)
方法進行保存。
- View進行繪制前,首先會調(diào)用
注意:測量動作完成后通過
getMeasuredWidth()
,getMeasuredHeight()
方法獲得的寬高值,即為此處設置的measuredWidth和measuredHeight。
2.onMeasure()方法中的兩個參數(shù)widthMeasureSpec,heightMeasureSpec封裝了父容器對View布局上的限制,內(nèi)部提供了寬高的信息(SpecMode,SpecSize),SpecSize是指在某種SpecMode下的參考尺寸,其中SpecMode有三種模式:
模式 | 含義 |
---|---|
EXACTLY | 父容器已經(jīng)檢測出view所需的大小 |
AT_MOST | 父容器指定了一個大小, view 的大小不能大于這個值 |
UNSPECIFIED | 父容器不對 view 有任何限制,要多大給多大 |
通過
MeasureSpec.getMode(widthMeasureSpec)
,MeasureSpec.getSize(widthMeasureSpec)
方法可分別獲得寬對應的模式和具體值。
對于應用層 View ,其 MeasureSpec 由父容器的 MeasureSpec 和自身 的 LayoutParams 來共同決定,針對不同的父容器和view本身不同的LayoutParams,view有多種MeasureSpec。其關系如下圖所示:
3.onMeasure()中measuredWidth和measuredHeight的計算方式,這里不做詳細分析,查看源碼再結合上面的MeasureSpec即可清楚。此處再概括一下Measure的測量流程,如下圖:
-
Layout--(onLayout(boolean changed,int l, int t, int r, int b))
- ViewGroup通過Layout()方法,來確定子元素的擺放位置。實際調(diào)用onLayout()來完成此動作。當viewgroup的位置被確定后,會在onLayout()方法中遍歷所有child并調(diào)用對應的layout(int l, int t, int r, int b)方法,從而確定子View的擺放位置。
2.在Layout(boolean changed,int l, int t, int r, int b)方法中指定的l、t、r、b為子控件的相對于父控件擺放位置,如下圖所示,坐標原點為父控件的左上角。
注意:在View中
getWidth()
和getHeight()
獲得的寬高,為父控件指定的擺放位置的寬高,即width = right - left,height = bottom - top,與getMeasuredWidth()
和getMeasuredHeight()
不同。一般來說,建議使用child.layout(0,0,chile.getMeasuredWidth(),child.getMeasureHeight())來指定子View的擺放位置。此時在子View中,getWidth() 與getMeasuredWidth()相等。
-
Draw--onDraw(Canvas canvas)
當測量(measure),和布局(layout)結束后,就進行View的繪制(draw)了,View通過draw(Canvas canvas)方法繪制圖像,實際調(diào)用的是onDraw(Canvas,canvas)方法。
draw 的大致流程:
a. 畫背景 background.draw(canvas)
b. 繪制自己( onDraw )
c. 繪制 children ( dispatchDraw )
d. 繪制裝飾( onDrawScrollBars )
備注:dispatchDraw 會遍歷調(diào)用所有 child 的 draw ,如此 draw 事件就一層層地傳遞了下去