View的簡(jiǎn)介 - Because if I don’t write it down
View的工作流程分為兩部分,第一部分 顯示在屏幕上的過(guò)程, 第二部分 響應(yīng)屏幕上的觸摸事件的過(guò)程。
對(duì)于顯示在屏幕上的過(guò)程:是View 從無(wú)到有,經(jīng)過(guò)測(cè)量大小(Measure)、確定在屏幕中的位置(Layout)、以及最終繪制在屏幕上(Draw) 這一系列的過(guò)程。
Measure() ?Layout()方法是final修飾的,無(wú)法重寫(xiě) ,Draw()雖然不是final的,但是也不建議重寫(xiě)該方法。
根據(jù)繼承對(duì)象的不同自定義View又分為繼承View 與ViewGroup兩種。
Mesaure
setMeasuredDimension設(shè)置測(cè)量的View的寬/高
onMeasure通過(guò)父View傳遞過(guò)來(lái)的大小和模式,以及自身的背景圖片的大小得出自身最終的大小,通過(guò)setMeasuredDimension()方法設(shè)置給mMeasuredWidth和mMeasuredHeight。
View的onMeasure邏輯大同小異,基本都是測(cè)量自身內(nèi)容和背景,然后根據(jù)父View傳遞過(guò)來(lái)的MeasureSpec進(jìn)行最終的大小判定,例如TextView會(huì)根據(jù)文字的長(zhǎng)度,文字的大小,文字行高,文字的行寬,顯示方式,背景圖片,以及父View傳遞過(guò)來(lái)的模式和大小最終確定自身的大小
ViewGroup本身沒(méi)有實(shí)現(xiàn)onMeasure,但是他的子類都有各自的實(shí)現(xiàn),通常他們都是通過(guò)measureChildWithMargins函數(shù)或者其他類似于measureChild的函數(shù)來(lái)遍歷測(cè)量子View,被GONE的子View將不參與測(cè)量,當(dāng)所有的子View都測(cè)量完畢后,才根據(jù)父View傳遞過(guò)來(lái)的模式和大小來(lái)最終決定自身的大小。
ViewGroup一般都在測(cè)量完所有子View后才會(huì)調(diào)用setMeasuredDimension()設(shè)置自身大小。
經(jīng)過(guò)measure 完成后,我們就可以通過(guò)getMeasuredWidth/Height 獲取View 的寬高。
Layout
Layout() 方法如果是ViewGroup,則循環(huán)遍歷所有子View,普通View則空實(shí)現(xiàn),因此如果我們繼承ViewGroup 我們需要遍歷執(zhí)行所有的child.layout()。
子View的位置通常還受有其他屬性左右,例如父View的orientation,gravity,自身的margin等等,影響布局的因素非常多。
Layout方法中接受四個(gè)參數(shù),是由父View提供,指定了子View在父View中的左、上、右、下的位置。父View在指定子View的位置時(shí)通常會(huì)根據(jù)子View在measure中測(cè)量的大小來(lái)決定。
onLayout是ViewGroup用來(lái)決定子View擺放位置的,各種布局的差異都在該方法中得到了體現(xiàn)。
onLayout比layout多一個(gè)參數(shù),changed,該參數(shù)是在setFrame通過(guò)比對(duì)上次的位置得出是否發(fā)生了變化,通常該參數(shù)沒(méi)有被使用的意義,因?yàn)楦竀iew位置和大小不變,并不能代表子View的位置和大小沒(méi)有發(fā)生改變。
Draw
draw()的過(guò)程就是繪制View到屏幕上的過(guò)程,draw()的執(zhí)行遵循如下步驟:
* ? ? ?1. Draw the background(繪制背景)
*??????2.?If?necessary,?save?the?canvas'?layers?to?prepare?for?fading(保存畫(huà)布的圖層來(lái)準(zhǔn)備色變)
*??????3.?Draw?view's?content(繪制內(nèi)容)
*??????4.?Draw?children(繪制children)
*??????5.?If?necessary,?draw?the?fading?edges?and?restore?layers(畫(huà)出褪色的邊緣和恢復(fù)層)
*? ? ? 6. Draw decorations (scrollbars for instance)(繪制裝飾 比如scollbar)
幾個(gè)比較重要的方法:
requestLayout:
當(dāng)view確定自身已經(jīng)不再適合現(xiàn)有的區(qū)域時(shí),該view本身調(diào)用這個(gè)方法要求parent view(父類的視圖)重新調(diào)用他的onMeasure onLayout來(lái)重新設(shè)置自己位置。特別是當(dāng)view的layoutparameter發(fā)生改變,并且它的值還沒(méi)能應(yīng)用到view上時(shí),這時(shí)候適合調(diào)用這個(gè)方法。
postInvalidate 與invalidate
界面刷新 onDraw方法會(huì)執(zhí)行,區(qū)別就是Invalidate不能直接在線程中調(diào)用,因?yàn)樗沁`背了單線程模型:Android UI操作并不是線程安全的,并且這些操作必須在UI線程中調(diào)用。 鑒于此,如果要使用invalidate的刷新,那我們就得配合handler的使用,使異步非ui線程轉(zhuǎn)到ui線程中調(diào)用,如果要在非ui線程中直接使用就調(diào)用postInvalidate方法即可,這樣就省去使用handler的煩惱。
getMeasurewidth()/getMeasureHeight()和getWidth/geiHeight()有什么不同?
在當(dāng)屏幕可以包裹內(nèi)容的時(shí)候,他們的值相等,
只有當(dāng)view超出屏幕后,才能看出他們的區(qū)別:
getMeasuredHeight()是實(shí)際View的大小,與屏幕無(wú)關(guān),
而getHeight的大小此時(shí)則是屏幕的大小。
當(dāng)超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外沒(méi)有顯示的大小