版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
目錄9.1ViewRoot和DecorView9.2理解MeasureSpec9.3View的工作流程9.4自定義View
九、View的工作原理九、View的工作原理ViewRoot對應于ViewRootImpl類,它是連接WindowManager和DecorView的紐帶,View的三大流程均是通過ViewRoot來完成的。在ActivityThread中,當Activity對象被創(chuàng)建完畢后,會將DecorView添加到Window中,同時會創(chuàng)建ViewGroup對象,并將ViewRootImpl對象和DecorView建立關系。View的繪制流程是從ViewRoot的performTraversals方法開始的,它經(jīng)過measure、layout和draw三個過程才能最終將一個View繪制出來,其中measure用來測量View的寬高,layout用來確定View在父容器中的放置位置,而draw則負責將View繪制在屏幕上。針對performTraversals的大致流程,如圖9.1所示。9.1ViewRoot和DecorView九、View的工作原理圖9.1九、View的工作原理performTraversals會移除調用performMeasure、performLayout和performDraw,這三個方法分別完成頂級View的measure、layout和draw這三大流程,其中在performMeasure中會調用measure方法,在measure方法中又會調用onMeasure方法,在onMeasure方法中則會對所有的子元素進行measure過程,這個時候measure流程就從父容器傳遞到子元素了,這樣就完成了一次measure過程。接著子元素會重復父容器的measure過程,如此反復就完成了整個View樹的遍歷。同理,performLayout和perform的傳遞流程和performMeasure是類似的,唯一不同的是,performDraw的傳遞過程是在draw方法中通過dispatchDraw來實現(xiàn)的。九、View的工作原理Measure過程中決定了View的寬高,Measure完成之后,可以通過getMeasureWidth和getMeasureHeight方法來獲得View的測量寬高,在幾乎所有的情況下它都等同于View的最終寬高,特殊情況除外。Layout過程決定了View的四個頂點的坐標和實際的View的寬高,完成以后,可以通過getTop、getBottom、getLLeft、getRight來得到View的四個頂點的位置,并可以通過getWidth和getHeight來得到View的最終寬高。Draw過程則決定了View的顯示,只有Draw方法完成以后View的內容才能呈現(xiàn)在屏幕上。九、View的工作原理
如圖9.2所示,DecorView作為View,一般情況下它內部包含了一個豎直方向的LineraLayout,在這個LinearLayout里面有上下兩個部分,上面為標題欄,下面為內容欄。在Activity中我們通過setContentView所設置的布局文件其實就是被加到內容欄中去了。圖9.2九、View的工作原理MeasureSpec代表一個32位int值。高2位代表SpecMode,低30位代表SpecSize。SpecMode是指測量模式,而SpecSize指在某種測量模式下的規(guī)格大小。9.2理解MeasureSpec9.2.1MeasureSpec九、View的工作原理九、View的工作原理
MeasureSpec通過將SpecMode和SpecSize打包成一個int值來避免過多的對象內存分配。為了方便操作,其提供了打包和解包操作,SpecMode和SpecSize也是一個int值。SpecMode有三類:。
UNSPECIFIED:父容器不對View有任何限制,要多大給多大,這種情況一般用于系統(tǒng)內部,表示一種測量的狀態(tài)。
EXACTLY:父容器已經(jīng)檢測出View所需要的精確大小,這個時候最終大小就是SpecSize所指定的值,它對應于LayoutParams中的matchparent和具體的數(shù)值兩種模式。
AT_MOST:父容器指定了一個可用的大小即SpecSize,View的大小不能大于這個值,具體是多大看不同的View的具體實現(xiàn)。它對應于LayoutParams中的wrap_content。九、View的工作原理
在View測量的時候,系統(tǒng)會將LayoutParams在父容器的約束下轉換成對應的MeasureSpec,然后再根據(jù)這個MeasureSpec來確定View測量后的寬高。
對于DecorView來說,在ViewRootImpl(注意這個類是隱藏的,需要在resoure文件中去找)中的measureHierarchy方法中有如下一段代碼,它展示了DecorView的MeasureSpec的創(chuàng)建過程,其中desiredWindowWidth和desiredWindowHeight是屏幕的尺寸。9.2.2MeasureSpec和Layoutparams的對應關系九、View的工作原理
九、View的工作原理DecorView的MeasureSpec的產(chǎn)生過程遵守了如下規(guī)則,根據(jù)它的LayoutParams中的寬高的參數(shù)來劃分。
?LayoutParams.MATCH_PARENT:精確模式,大小就是窗口的大?。?/p>
?LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超過窗口的大小;
固定大?。壕_模式,大小為LayoutParams中指定的大小。
對于普通的View來說,這里是指我們布局中的View,View的measure過程由ViewGroup傳遞而來,ViewGroup的measureChildWithMargins方法:九、View的工作原理
上述方法會對子元素進行measure,在調用子元素的measure方法之前會先通過getChildMeasureSpec方法得到資源的MeasureSpec。從代碼來看,很顯然,子元素的MeasureSpec的創(chuàng)建與父容器的MeasureSpec和子元素自身的LayoutParams有關,此外還和View的margin和padding有關。具體看ViewGroup的getChildMeasureSpec方法。如下所示:九、View的工作原理
九、View的工作原理九、View的工作原理九、View的工作原理
上述方法主要作用是根據(jù)父容器的MeasureSpec同時結合View本身的LayoutParams的參數(shù)來確定子元素的MeasureSpec,參數(shù)中的padding是指父容器已占用的控件大小,因此子元素可用的大小為父容器的尺寸減去padding,具體的代碼如下:getChildMeasureSpec清楚地展示了普通View的MeasureSpec的創(chuàng)建規(guī)則。表9.1對getChildMeasureSpec的工作原理進行了梳理。表中的ParentSize是指父容器中目前可用的大小。九、View的工作原理childLayoutParamsparentSpecModeEXACTLYAT_MOSTUNSPECIFIEDdp/pxEXACTLYchildSizeEXACTLYchildSizeEXACTLYchildSizeMatch_parentEXACTLYparentSizeAT_MOSTparentSizeUNSPECIFIEDoWarp_contentAT_MOSTparentSizeAT_MOSTparentSizeUNSPECIFIEDo表9.1普通View的MeasureSpec的創(chuàng)建規(guī)則
只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以快速地確定子元素的MeasureSpec了,有了MeasureSpec就可以進一步確定出子元素測量的大小了。九、View的工作原理
1.View的Measure過程View的Measure過程由其Measure方法來完成。Measure方法是一個final類型的方法,意味著子類不能重寫此方法。在View的Measure方法中會去調用View的onMeasure方法,因此只需要看onMeasure的實現(xiàn)即可。View的onMeasure方法如下:9.3View的工作流程9.3.1Measure過程九、View的工作原理
getDefaultSize方法:九、View的工作原理getDefaultSize返回的大小就是measureSpec中的specSize,而這個specSize就是View測量后的大小。這里多次提到測量后的大小,是因為View最終的大小是在layout階段確定的,所以這里必須要加以區(qū)分,但不是所有情況下的View的測量大小都與最終大小相等。UNSPECIFIED一般用于系統(tǒng)內部的測量過程,在這種情況下,View的大小為getDefaultSize的第一個參數(shù),即寬高分別為getSuggestedMinimumWidth和getSuggestedMinimumHeight這兩個方法的返回值,看一下它們的源碼:九、View的工作原理
可以看出,如果View沒有設置任何背景,那么View的寬度為mMinWidth,而mMinWidth對應于android:minWidth這個屬性所指定的值,因此View的寬度即為android:minWidth屬性所指定的值。這個屬性如果不知道,那么mMinWidth則默認為0;如果View指定了背景,則View的寬度為max(mMinWidth,mBackground.getMinimumWidth())。我們看一下Drawable的getMinimumWidth()方法,如下所示:九、View的工作原理
可以看出,getMinimumWidth返回的就是Drawable的原始寬度,前提是這個Drawable有原始寬度,否則就返回0。那么Drawable在什么情況下有原始寬度呢?ShapeDrawable無原始寬高,而BitmapDrawable有原始寬高。
從getDefault方法的實現(xiàn)來看,View的寬高由SpecSize決定,所以我們可以得出以下結論:直接繼承View的自定義控件需要重寫onMeasure方法并設置wrap_content時的自身參數(shù),否則在布局文件中使用wrap_content就相當于match_parent。九、View的工作原理
2.ViewGroup的measure過程
對于ViewGroup來說,除了完成自己的measure過程以外,還會去調用所有子元素的measure方法,各個子元素再遞歸去執(zhí)行這個過程。和View不同的是,ViewGroup是一個抽象類,因此它沒有重寫View的onMeasure方法,但是它提供了一個叫measureChildren的方法:九、View的工作原理ViewGroup在measure時會對每一個子元素進行measure,measureChild方法的實現(xiàn):measureChild就是取出子元素的LayoutParams,然后再通過getChildMeasureSpec來創(chuàng)建子元素的MeasureSpec,接著MeasureSpec直接傳遞給View的measure方法來進行測量。九、View的工作原理
不同的ViewGroup子類有不同的布局特性,這導致它們的測量細節(jié)都不相同。LinearLayout的onMeasure方法:
上述代碼對不同的布局方向做了不同的測量,選擇查看垂直布局的LinearLayout測量過程,即measureVertical方法。measureVertical方法中重要代碼如下:九、View的工作原理
系統(tǒng)會遍歷子元素并對子元素執(zhí)行measureChild-BeforeLayout方法,調用子元素的measure方法,各個子元素就開始依次進入measure過程,并且系統(tǒng)會通過mTotalLength這個變量來存儲LinearLayout在豎直方向的初步高度。每測量一個子元素,mTotalLength就會增加包括子元素的高度以及子元素在豎直方向上的margin等。當子元素測量完畢后,LinearLayout會測量自己的大小,源碼如下:九、View的工作原理
當子元素測量完畢后,LinearLayout會根據(jù)子元素的情況來測量自己的大小,針對豎直的LinearLayout而言,它在水平方向的測量過程遵循View的測量過程,在豎直方向的測量過程與View有所不同。最終高度還需要考慮其他在豎直方向的padding,這個過程可以進一步參看源碼:九、View的工作原理九、View的工作原理如果在Activity已啟動的時候就做一個任務,但是這個任務需要獲取這個View的寬高。實際上在onCreate或者onStart、onResume中均無法正確得到某個View的寬高信息,這是因為View的measure和Activity的生命周期方法不是同步執(zhí)行的,一次無法保證Activity執(zhí)行了onCreate、onStart、onResume時某個View已經(jīng)測量完畢。如果View還沒有測量完畢,那么獲得的寬高就是0。給出四種方法來解決這個問題。九、View的工作原理1)?onWindowFocusChangedView已經(jīng)初始化完畢了,寬高已經(jīng)準備好了,這個時候去獲取寬高沒有問題。onWindowFocusChanged會被調用多次,當Activity的窗口得到焦點和失去焦點時均會被調用一次。當Activity繼續(xù)執(zhí)行和暫停執(zhí)行時,onWindowFocusChanged均會被調用,如果頻繁地進行onResume和onPause,那么onWindowFocusChanged也會被頻繁地調用。2)?View.post(runnable)
通過post可以將一個runnable投遞到消息隊列的尾部,然后等待Looper調用此runnable的時候,View也已經(jīng)初始化了。典型代碼如下:九、View的工作原理3)?ViewTreeObserver
使用ViewTreeObserver的眾多回調方法可以完成這個功能,比如使用onGlobalLLayoutListener這個接口,當View樹的狀態(tài)發(fā)生改變或者View樹內部的View可見性發(fā)生改變時,onGlobalLyaout方法將被回調,因此這是獲取View寬高的一個很好的時機。需要注意的是,伴隨View樹的狀態(tài)改變等,onGlobalLayout會被調用多次。典型代碼如下:九、View的工作原理九、View的工作原理Layout的作用是ViewGroup用來確定子元素的位置,當ViewGroup的位置被確定后,它在onLayout中會遍歷所有的子元素并調用其Layout方法。在Layout方法中onLayout方法會被調用。Layout過程比measure過程簡單,在Layout方法中確定View本身的位置,而onLayout方法則會確定所有子元素的位置。View的Layout方法如下所示:9.3.2Layout過程九、View的工作原理九、View的工作原理
Layout方法的大致流程:首先通過setFrame方法來設定View的四個頂點的位置,即初始化mLeft、mRight、mTop、mBottom這四個值。View的四個頂點一旦確定,那么View在父容器中的位置就確定了,接著會調用onLayout方法。這個方法的用途是父容器確定子元素的位置,和onMeasure方法類似,onLayout的具體實現(xiàn)同樣和具體的布局相關,那么View和ViewGroup均沒有真正實現(xiàn)onLayout。接下來,我們可以看一下LinearLayout的onLayout方法,如下所示:九、View的工作原理
LinearLayout中的Layout的實現(xiàn)邏輯和onMeasure的實現(xiàn)邏輯類似:九、View的工作原理九、View的工作原理
此方法會遍歷所有子元素并調用setChildFrame方法來為子元素指定對應的位置,其中childTop會慢慢增大,這就意味著后面的子元素會被放置在靠下的位置,這剛好符合豎直方向的linearLayout的特性。至于setChildFrame,它僅僅是調用子元素的layout方法而已,這樣父元素在子元素layout方法中完成自己的定位以后,就通過onlayout方法去調用子元素的layout方法,子元素又會通過自己的layout方法來確定自己的位置,這樣一層一層地傳遞下去就完成了整個View樹的layout過程。setChildFrame方法的實現(xiàn)如下所示:九、View的工作原理
在setChildFrame中的width和height實際上就是子元素的測量寬高。九、View的工作原理
Draw過程的作用是將View繪制到屏幕上面。View的繪制過程遵循如下幾步:(1)繪制背景background.draw(canvas)。(2)繪制自己(onDraw)。(3)繪制children(dispatchDraw)。(4)繪制裝飾(onDrawScrollBars)。
當需要使用自定義View時,都需要實現(xiàn)該方法,9.3.3Draw過程九、View的工作原理
繼承View主要用于實現(xiàn)一些不規(guī)則的效果,即這些效果不方便通過布局的組合方式來達到,往往需要靜態(tài)或者動態(tài)地顯示一些不規(guī)則的圖形。很顯然這需要通過繪制的方式來實現(xiàn),即重寫onDraw方法。采用這種方式需要自己支持wrap_content,并且padding也需要自己處理。9.4自定義View9.4.1繼承View九、View的工作原理
定義一個MyCustomView類,該類繼承了View:九、View的工作原理九、View的工作原理
在MyCustomView的構造方法中得到了background.png圖片的位圖對象,在onDraw方法里創(chuàng)建了一個paint對象,同時繪制一塊矩形,該矩形的大小由viewWidth、viewHeight決定,viewWidth以及viewHeight在第一次加載MyCustomView時得到它們的初始值,也就是通過onSizeChanged()方法得到它的值。最后通過canvus.drawBitmap()把圖片繪制到指定的區(qū)域。我們需要將該View放入布局文件中去,Activity_main.xml代碼如下:九、View的工作原理九、View的工作原理
在布局文件中部署該自定義View的時候,需要將包名以及類名都寫進去。如果僅僅是寫入MyCustomView將會出現(xiàn)錯誤。
運行程序,出現(xiàn)如這樣的結果:圖9.3九、View的工作原理
如圖9.3所示,在textView的區(qū)域下面繪制了一塊紅色區(qū)域,在該區(qū)域的左上角有一個圖片,即background.xml,這樣簡單地實現(xiàn)了自定義View。接下來我們將在此基礎上更加復雜化。我們希望上圖中那塊灰色的區(qū)域能夠填充整個紅色區(qū)域:九、View的工作原理九、View的工作原理
在修改部分,使用雙層for循環(huán),在外層循環(huán),繪制圖片的寬度,內層循環(huán)繪制圖片的高度。
執(zhí)行上述修改后的代碼可以看到,整個區(qū)域都已經(jīng)填充該圖片。接下來,我們將在這些圖片上繪制圖9.4,并且居中。在以往的布局文件中,我們可以通過控件的屬性設置控件的位置。在自定義控件內,沒有這些屬性設置,需要我們自己去計算。圖9.4九、View的工作原理
計算這個自定義View的中間位置,在onDraw()方法中添加如下代碼:九、View的工作原理
整個過程共分為三步:首先需要得到該圖片的位圖對象;同時需要繪制的坐標信息;當有了上述兩個條件之后,通過canvus方法就可繪制圖片、顯示圖片上的文字了。類似于Android自定義的TextView,運行結果:圖9.5九、View的工作原理
如圖9.5所示,在自定義View上加載了“瘋狂英語”文字,接下來,動態(tài)的顯示文字,在上述demo中,在界面的底部逐個顯示一行字。文字圖片如圖:圖9.6九、View的工作原理
想要“隨時隨地學英語”這幾個字慢慢地顯示出來,而不是一下全部顯示出來,這樣就形成了一個動態(tài)的效果。在傳統(tǒng)的控件中,TextView無法實現(xiàn)該效果,而在自定義View中可以完成:九、View的工作原理運行程序:圖9.7九、View的工作原理
結果如圖9.7所示,動態(tài)文字內容顯示出來,繼續(xù)修改,用到線程:九、View的工作原理
讓MyCustomView實現(xiàn)Runnable接口,定義一個圖片顯示區(qū)域控制模塊參數(shù)index。利用線程刷新index參數(shù),從而控制圖片顯示區(qū)域。讓圖片中的文字達到慢慢顯示的后果。線程代碼如下:九、View的工作原理
在主線程刷新UI使用的是Invalidate()方法,在此案例中index的變化是在子線程中,故使用的是postInvalidate();。
在MyCustomView的構造方法中開啟線程,代碼如下:九、View的工作原理
當加載該自定義View時,開啟工作線程,改變index值的大小,同時調用postInvalidate()方法刷新UI,每次刷新UI時都將重新調用onDraw()方法,從而動態(tài)顯示文字。在onDraw()中增加代碼如下:九、View的工作原理案例:使用自定義View來實現(xiàn)一個復雜的控件——繪制股票的趨勢圖。1.實時股價-時間界面
一個股票趨勢圖包含有時間、價格以及趨勢走向等因素,我們一個一個來完成它。首先完成在屏幕下方繪制時間。模擬一組數(shù)據(jù)用于繪制時間。
自定義View類代碼如下:九、View的工作原理九、View的工作原理九、View的工作原理
在自定義View類中,我們定義了一個集合lists保存股票價格點,在lists中保存的是HashMap對象。之所以用到HashMap,是由于股票價格點同時包含了時間及價格。在自定義類還自定義了setData()方法,該方法用于設置股票價格數(shù)據(jù)。在onSizeChanged()方法中得到自定義控件的長度。最好在onDraw()方法中繪制時間。
注意:在每個方法中都利用Log做了日志輸出,當運行程序時可以更直觀地看到程序運行的大致方法調用過程。MainActivity.java文件代碼如下:九、View的工作原理九、View的工作原理
九、View的工作原理Activity_main.xml代碼如下:九、View的工作原理運行結果:圖9.8九、View的工作原理2.實時股價-價格界面
我們需要知道股價最大值,只有根據(jù)股價的最大值,才能得到每一時間點股價與最大值的比例,從而計算出該點在自定義View中的位置,所以首先我們需要計算出這些時刻點的最大股價。九、View的工作原理九、View的工作原理九、View的工作原理九、View的工作原理九、View的工作原理運行程序:圖9.10九、View的工作原理
3.實時股價-動態(tài)展示界面
如圖9.10所示,股票的時間點、價格、連線都已經(jīng)連接起來了。如何讓數(shù)據(jù)動態(tài)的顯示,是我們接下來的任務。在上面的代碼中,數(shù)據(jù)來源于MainActivity的onCreate()方法,固定的數(shù)據(jù),我們需要動態(tài)顯示它。此處用到的是Random對象。在getDate()中修改代碼如下:九、View的工作原理九、View的工作原理
通過調用Random對象的nextInt(n)對象,來產(chǎn)生一個大于等于0小于n的隨機數(shù),這樣就實現(xiàn)了動態(tài)數(shù)據(jù)。在真實的項目中,用到的是網(wǎng)絡數(shù)據(jù),需要在MyCustomView顯示:九、View的工作原理九、View的工作原理運行程序,出現(xiàn)動態(tài)的實時股市圖:圖9.11九、View的工作原理
這種方法主要用于實現(xiàn)自定
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度美容院員工社會保險繳納合同樣本4篇
- 課題申報參考:面向2035年高等教育布局結構研究
- 民政局2025年離婚協(xié)議書起草與備案流程指導4篇
- 2025年度門頭房屋租賃合同含租賃用途及經(jīng)營方向限制4篇
- 河南省周口中英文學校高三上學期期中考試語文試題(含答案)
- 2025年度個人二手房交易反擔保合同規(guī)范2篇
- 2025年度個人汽車貨運風險分擔合同范本
- 2025年度門禁監(jiān)控設備生產(chǎn)與銷售合同8篇
- 2025年度水電工程合同履約監(jiān)管承包協(xié)議4篇
- 2025年度木結構建筑綠色施工與環(huán)保驗收合同4篇
- 喬遷新居結婚典禮主持詞
- 小學四年級數(shù)學競賽試題(附答案)
- 魯科版高中化學必修2全冊教案
- 人口分布 高一地理下學期人教版 必修第二冊
- 子宮內膜異位癥診療指南
- 教案:第三章 公共管理職能(《公共管理學》課程)
- 諾和關懷俱樂部對外介紹
- 玩轉數(shù)和形課件
- 保定市縣級地圖PPT可編輯矢量行政區(qū)劃(河北省)
- 新蘇教版科學六年級下冊全冊教案(含反思)
- 天然飲用山泉水項目投資規(guī)劃建設方案
評論
0/150
提交評論