2020年2月22.距離新年已通過去了大半個月了,依舊的出不了門,依舊的躲在家裏每日三餐,依舊的在家辦公,也不知道下週會不會復工,再次彙總手中各類保存整理的筆記git
相關內容後續GitHub更新,想衝擊金三銀四的小夥伴能夠找找看看,歡迎star
(順手留下GitHub連接,須要獲取相關面試等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)github
View的繪製是從上往下一層層迭代下來的。DecorView
-->ViewGroup
(--- >ViewGroup
)-->View
,按照這個流程從上往下,依次measure
(測量),layout
(布 局),draw
(繪製)面試
顧名思義,就是測量每一個控件的大小。canvas
調用measure()
方法,進行一些邏輯處理,而後調用onMeasure()
方法,在其中調用 setMeasuredDimension()
設定View的寬高信息,完成View的測量操做。框架
public final void measure(int widthMeasureSpec, int heightMeasur eSpec) { }
measure()
方法中,傳入了兩個參數 widthMeasureSpec, heightMeasureSpec
表示 View的寬高的一些信息。ide
protected void onMeasure(int widthMeasureSpec, int heightMeasure Spec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumW idth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heig htMeasureSpec)); }
由上述流程來看Measure
流程很簡單,關鍵點是在於widthMeasureSpec
, heightMeasureSpec
這兩個參數信息怎麼得到? 佈局
若是有了widthMeasureSpec
, heightMeasureSpec
,經過必定的處理(能夠重寫,自 定義處理步驟),從中獲取View的寬/高,調用setMeasuredDimension()
方法,指定 View的寬高,完成測量工做。優化
MeasureSpec
的肯定先介紹下什麼是MeasureSpec
?MeasureSpec
由兩部分組成,一部分是測量模式,另外一部分是測量的尺寸大
小。 其中,Mode模式共分爲三類spa
UNSPECIFIED
: 不對View進行任何限制,要多大給多大,通常用於系統內部3d
EXACTLY
: 對應LayoutParams
中的match_parent
和具體數值這兩種模式。檢測到 View所須要的精確大小,這時候View的最終大小就是SpecSize
所指定的值,
AT_MOST
: 對應LayoutParams
中的wrap_content
。View的大小不能大於父容器 的大小。
那麼MeasureSpec
又是如何肯定的?
對於DecorView
,其肯定是經過屏幕的大小,和自身的佈局參數LayoutParams
。
這部分很簡單,根據LayoutParams
的佈局格式(match_parent
,wrap_content
或 指定大小),將自身大小,和屏幕大小相比,設置一個不超過屏幕大小的寬高,以 及對應模式。 對於其餘View(包括ViewGroup
),其肯定是經過父佈局的MeasureSpec
和自身的 佈局參數LayoutParams
。 這部分比較複雜。如下列圖表表示不一樣的狀況:
當子View的LayoutParams
的佈局格式是wrap_content
,能夠看到子View的大小 是父View的剩餘尺寸,和設置成match_parent
時,子View的大小沒有區別。爲了 顯示區別,通常在自定義View時,須要重寫onMeasure
方法,處理wrap_content
時的狀況,進行特別指定。
從這裏看出MeasureSpec
的指定也是從頂層佈局開始一層層往下去,父佈局影響 子佈局。
可能關於MeasureSpec
如何肯定View大小還有些模糊,篇幅有限,沒詳細具體展開介紹
View的測量流程:
測量完View大小後,就須要將View佈局在Window中,View的佈局主要經過肯定上 下左右四個點來肯定的。
其中佈局也是自上而下,不一樣的是ViewGroup
先在layout()
中肯定本身的佈局,然 後在onLayout()
方法中再調用子View的layout()
方法,讓子View佈局。在Measure 過程當中,ViewGroup
通常是先測量子View的大小,而後再肯定自身的大小。
public void layout(int l, int t, int r, int b) { // 當前視圖的四個頂點 int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // setFrame() / setOpticalFrame():肯定View自身的位置 // 即初始化四個頂點的值,而後判斷當前View大小和位置是否發生了變化並返回 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); //若是視圖的大小和位置發生變化,會調用onLayout() if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PF LAG_LAYOUT_REQUIRED) { // onLayout():肯定該View全部的子View在父容器的位置 onLayout(changed, l, t, r, b); ... }
上面看出經過 setFrame()
/ setOpticalFrame()
:肯定View自身的位置,經過 onLayout()
肯定子View的佈局。 setOpticalFrame()
內部也是調用了 setFrame()
,因此具體看setFrame()
怎麼肯定自身的位置佈局。
protected boolean setFrame(int left, int top, int right, int bot tom) { ... // 經過如下賦值語句記錄下了視圖的位置信息,即肯定View的四個頂點 // 即肯定了視圖的位置 mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBott om); }
肯定了自身的位置後,就要經過onLayout()
肯定子View的佈局。onLayout()
是一個 可繼承的空方法。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
若是當前View就是一個單一的View,那麼沒有子View,就不須要實現該方法。
若是當前View是一個ViewGroup
,就須要實現onLayout
方法,該方法的實現個自 定義ViewGroup
時其特性有關,必須本身實現。
由此便完成了一層層的的佈局工做。 View的佈局流程:
View的繪製過程遵循以下幾步:
①繪製背景 background.draw(
canvas
)
②繪製本身(onDraw
)
③繪製Children(dispatchDraw
)
④繪製裝飾(onDrawScrollBars
)
從源碼中能夠清楚地看出繪製的順序。
public void draw(Canvas canvas) { // 全部的視圖最終都是調用 View 的 draw ()繪製視圖( ViewGroup 沒有複寫 此方法) // 在自定義View時,不該該複寫該方法,而是複寫 onDraw(Canvas) 方法進行繪 制。 // 若是自定義的視圖確實要複寫該方法,那麼須要先調用 super.draw(canvas)完 成系統的繪製,而後再進行自定義的繪製。 ... int saveCount; if (!dirtyOpaque) { // 步驟1: 繪製自己View背景 drawBackground(canvas); } // 若是有必要,就保存圖層(還有一個復原圖層) // 優化技巧: // 當不須要繪製 Layer 時,「保存圖層「和「復原圖層「這兩步會跳過 // 所以在繪製的時候,節省 layer 能夠提升繪製效率 final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) // 步驟2:繪製自己View內容 默認爲空實現, 自定義View時需 要進行復寫 onDraw(canvas); ...... // 步驟3:繪製子View 默認爲空實現 單一View中不須要實現,ViewG roup中已經實現該方法 dispatchDraw(canvas); ........ // 步驟4:繪製滑動條和前景色等等 onDrawScrollBars(canvas); .......... return; } ... }
不管是ViewGroup
仍是單一的View,都須要實現這套流程,不一樣的是,在 ViewGroup
中,實現了 dispatchDraw()
方法,而在單一子View中不須要實現該方 法。自定義View通常要重寫onDraw()
方法,在其中繪製不一樣的樣式。
View繪製流程:
從View的測量、佈局和繪製原理來看,要實現自定義View,根據自定義View的種 類不一樣,可能分別要自定義實現不一樣的方法。可是這些方法不外乎:onMeasure()
方法,onLayout()
方法,onDraw()
方法。
onMeasure()
方法: 單一View,通常重寫此方法,針對wrap_content
狀況,規定 View默認的大小值,避免於match_parent狀況一致。ViewGroup
,若不重寫,就會 執行和單子View中相同邏輯,不會測量子View。通常會重寫onMeasure()
方法,循 環測量子View。
onLayout()
方法: 單一View,不須要實現該方法。ViewGroup
必須實現,該方法是 個抽象方法,實現該方法,來對子View進行佈局。
onDraw()
方法: 不管單一View,或者ViewGroup
都須要實現該方法,因其是個空 方法
本身整理的983頁面試大全,爲打算面試或者正在面試的人提供借鑑的思路
上圖知識彙總的PDF相關內容後續GitHub更新,想衝擊金三銀四的小夥伴能夠找找看看,歡迎star
(順手留下GitHub連接,須要獲取相關面試等內容的能夠本身去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)