今天我們來複習下View的繪製流程。View是Android中所有控件的基類,是控件的一種抽象,代表了一個控件。
Android進階——Android視圖工作機制之measure、layout、draw
一、View樹的繪製流程
measure –> layout –> draw
測量 –> 佈局 –>繪製
measure :測量出View的寬/高;
layout:確定View最終寬/高四個頂點的位置;
draw:將View繪製在屏幕上;
二、measure
1、ViewGroup.LayoutParams:
指定視圖寬度、高度的參數有 match_parent、wrap_content、固定值(100dp)三種。
2、MeasureSpec:
測量規則,代表一個32位的int值,將SpecMode、SpecSize包裝成一個int值來避免過多的對象內存分配;高2位代表SpecMode(測量模式), 低30位代表SpecSize(在某種測量模式下測量出的具體值)。
SpecMode 測量模式:
UNSPECIFIED:
父容器不對View有任何限制,要多大給多大,一般用於系統內部,表示一種測量狀態。EXACTLY:
父容器檢測出View所需要的精確大小,這時候View的值就是SpecSize;它對應於LayoutParams中的 match_parent 和 具體的數值兩種模式。AT_MOST:
父容器指定了一個可用大小即SpecSize,View的大小不能大於這個值;它對應於LayoutParams中的 wrap_content 模式。
結論:
子View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定的。
3、measure的重要方法
- measure(int widthMeasureSpec, int heightMeasureSpec)
是一個final 方法子類無法重寫,會調用onMeasure方法,我們可以在onMeasure方法中處理測量的邏輯。 - onMeasure(int widthMeasureSpec, int heightMeasureSpec)
在onMeasure方法中處理測量的邏輯。 - setMeasuredDimension(int measuredWidth, int measuredHeight)
測量的終極方法,測量完成後進行調用,傳入測量出來的的寬、高值。
三、layout
public void layout(int l, int t, int r, int b){}
確定View本身的位置。protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
確定所有子元素的位置;
父容器的位置確定後,它在onLayout中會遍歷所有的子元素並調用其layout方法,在layout方法中會調用onLayout方法。
四、draw
1、將View繪製在屏幕上,遵循如下幾步:
1、繪製背景:drawBackground(canvas);
2、繪製自己 : onDraw(canvas);
3、繪製childen :dispatchDraw(canvas);
4、繪製裝飾: onDrawForeground(canvas);
2、invalidate()、requestLayout()
invalidate()和requestLayout(),常用於View重繪和更新,其主要區別如下:
invalidate方法只會執行onDraw方法;
requestLayout方法只會執行onMeasure方法和onLayout方法,並不會執行onDraw方法;
所以當我們進行View更新時,若僅View的顯示內容發生改變且新顯示內容不影響View的大小、位置,則只需調用invalidate方法;若View寬高、位置發生改變且顯示內容不變,只需調用requestLayout方法;若兩者均發生改變,則需調用兩者,按照View的繪製流程,推薦先調用requestLayout方法再調用invalidate方法。
3、invalidate() 和 postInvalidate()
invalidate方法用於UI線程中重新繪製視圖
postInvalidate方法用於非UI線程中重新繪製視圖,省去使用handler