View的工作原理
文章目錄
1)View工作流程
2)measure過程
3)layout過程
4)draw過程
5)自定義View
View工作流程
- View的繪製流程是從
ViewRoot
和performTraversals
開始。- ViewRoot對應於ViewRootImpl類,它是連接WindowManager和DecorView的紐帶。
- performTraversals()依次調用
performMeasure()
、performLayout()和performDraw()三個方法,分別完成頂級 View的繪製。其中,performMeasure()會調用measure()
,measure()中又調用onMeasure()
,實現對其所有子元素的measure過程,這樣就完成了一次measure過程;接着子元素會重複父容器的measure過程,如此反覆至完成整個View樹的遍歷。layout和draw同理。 measure
測量->layout
佈局->draw
繪製
measure過程
- 確定View的測量寬高,對應
onMeasure()
方法。 - MeasureSpec
- 通過寬測量值widthMeasureSpec和高測量值heightMeasureSpec決定View的大小
- 一個32位int值,高2位代表SpecMode,低30位代表SpecSize。值由子View的佈局參數LayoutParams和父容器的MeasureSpec值共同決定。
- 三種模式
- UNSPECIFIED:父容器不對View有任何限制,要多大有多大。常用於系統內部。
- EXACTLY:對應LyaoutParams中的match_parent或具體數值。
- AT_MOST:View的大小不能大於SpecSize這個值。對應LayoutParams中的wrap_content。
- View的measure
- 只有一個原始的View,通過measure()即可完成測量
- ViewGroup的measure
- 除了完成ViewGroup自身的測量外,還會遍歷去調用所有子元素的measure方法。
- 使用measureChildren()
layout過程
- 確定View的最終寬高和四個頂點的位置,對應
onLayout()
方法。 - 單一View的layout過程
- 調用layout(),接着調用onLayout()
- ViewGroup的layout過程
- 從頂級View開始依次調用layout(),其中子View的layout()會調用setFrame()來設定自己的四個頂點(mLeft、mRight、mTop、mBottom),接着調用onLayout()來確定其座標
draw過程
- 將View 繪製到屏幕上,對應
onDraw()
三個方法。 - 單一View的draw過程
- 繪製順序:
- 1.繪製背景:
background.draw(canvas)
- 2.繪製自己:
onDraw(canvas)
- 3.繪製children:
dispatchDraw(canvas)
- 4.繪製裝飾:
onDrawScrollBars(canvas)
- 1.繪製背景:
- ViewGroup的draw過程
- ViewGroup繪製自身
- ViewGroup遍歷子View,然後繪製其所有子View
- 自上而下、一層層地傳遞下去,直到完成整個View樹的draw過程
自定義View
- 繼承View重寫onDraw方法
- 實現一些不規則的效果,重寫onDraw方法,需要自己支持wrap_content,並且padding也需要自己處理
- 繼承ViewGroup派生特殊的Layout
- 實現自定義的佈局
- 繼承特定的View
- 擴展某種已有的View功能,不需要自己實現wrap_content和padding
- 繼承特定的ViewGroup
- 不需要自己處理ViewGroup的測量和佈局這兩個過程。
- 注意事項
- 讓View支持wrap_content
- 讓View支持padding
- 不要在View中使用Handler
- View中如果有線程或者動畫,參考onDetachedFromWindow
- 處理好滑動衝突
參考
《Android開發藝術與探索》
開發藝術之View
自定義View基礎
自定義View Draw過程