自定義View知識點
自定義View我們需要了解View的繪製機制和ViewGroup的繪製機制,瞭解之後我們就可以在系統預留的方法裏進行自定義View的工作了。
1、MeasureSpec
模式 | 說明 |
---|---|
UNSPECIFIED | 父容器不對View有任何限制,給它想要的任何尺寸。一般用於系統內部,表示一種測量狀態。 |
EXACTLY | 父容器已經檢測出view的精確大小,這時候view的大小就是size所指定的值。它對應於LayoutParams中的match_parent和具體數值兩種模式。 |
AT_MOST | 父容器指定了一個可用大小,即size,子view的大小不能大於這個值,具體值要看vew的實現。它對應於LayoutParams中的wrap_content。 |
MeasureSpec是受到自身的LayoutParams和父容器的MeasureSpec共同影響的。
2、Measure
1、View的Measure過程
1.View調用onMeasure方法,onMeasure方法裏調用了setMeasuredDimension方法,setMeasuredDimension方法又調用了getDefaultSize方法。
2.調用setMeasuredDimension方法,該方法是用來設置View的寬高的。
3.setMeasuredDimension方法調用了getDefaultSize方法,該方法根據傳入的不同的MeasureSpec和size進行測量返回不同的測量結果。在AT_MOST和EXACTLY模式下,返回MeasureSpec.getSize方法返回的值,在UNSPEXIFIED方法裏返回傳入的大小值。根據上面的MeasureSpec表可知,對於直接繼承View自定義的View而言,wrap_content和match_parent是一樣的,所以我們要重寫onMeasure方法。
4.getDefaultSize方法傳入的參數調用了getSuggestedMinimumWidth()和getSuggestedMinimumHeight()方法。這兩個方法會根據View是否有背景來返回測量的值。如果沒有背景則返回mMinWidth或者mMinHeight,mMin的值是可以在View裏設置的,通過setMinimumWidth或者setMinimumHeight即可設置;如果有背景則返回mMinWidth或者mMinHeight和mBackground.getMinimumWidth或mBackground.getMinimumHeight中的最大值。
2、ViewGroup的Measure過程
1.ViewGroup不只要測量自身,還要便利測量子元素的measure方法。ViewGroup裏沒有提供onMeasure方法,但是它有measureChildren方法。
2.在measureChildren方法裏面它調用了getChildMeasureSpec方法。有意思的是我們可以在這張表裏找到一個switch語句,裏面有父View和子View測量模式最終返回的不同的結果,如圖所示
父View測量模式 | 子View LayoutParams | 最終測量模式 |
---|---|---|
EXACTLY | 具體值 | EXACTLY |
EXACTLY | MATCH_PARENT | EXACTLY |
EXACTLY | WRAP_CONTENT | AT_MOST |
AT_MOST | 具體值 | EXACTLY |
AT_MOST | MATCH_PARENT | AT_MOST |
AT_MOST | WRAP_CONTENT | AT_MOST |
UNSPECIFIED | 具體值 | EXACTLY |
UNSPECIFIED | MATCH_PARENT | UNSPECIFIED |
UNSPECIFIED | WRAP_CONTENT | UNSPECIFIED |
需要注意的是,當父容器的測量模式爲AT_MOST時,子View的LayoutParams爲WRAP_CONTENT,根據上圖則可以發現子元素的測量模式也是AT_MOST,要解決這個問題需要在LayoutParams裏制定以下默認的寬和高。
3、Layout
layout方法的作用是確定View的位置。
layout有四個參數,分別是l、t、r、b分帶是View從左上右下相對其父容器的位置。layout方法會調用setFrame方法,而setFrame方法也有四個參數,正是layout方法穿進去的四個參數。調用setFrame方法後會調用onLayout方法,而onLayout方法是個空方法。需要我們根據不同的需求去實現。
4、Draw
draw的流程如下:
1、繪製 backgroud(drawBackground) ;
2、如果需要的話,保存canvas的layer,來準備fading ;
3、繪製view的content(onDraw方法);
4、繪製children(dispatchDraw方法);
5、如果需要的話,繪製fading edges,然後還原layer ;
6、繪製裝飾器、比如scrollBar(onDrawForeground);