下載本文檔
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開發(fā)技術(shù)】淺談AndroidView繪制三大流程探索及常見問題
View繪制的三大流程,指的是measure(測(cè)量)、layout(布局)、draw(繪制)measure負(fù)責(zé)確定View的測(cè)量寬/高,也就是該View需要占用屏幕的大小,確定完View需要占用的屏幕大小后,就會(huì)通過layout確定View的最終寬/高和四個(gè)頂點(diǎn)在手機(jī)界面上的位置,等通過measure和layout過程確定了View的寬高和要顯示的位置后,就會(huì)執(zhí)行draw繪制View的內(nèi)容到手機(jī)屏幕上。在詳細(xì)介紹這三大流程之前,需要簡(jiǎn)單了解一下ViewRootImpl,View繪制的三大步驟都是通過ViewRootImpl實(shí)現(xiàn)的,ViewRootImpl是連接WindowManager窗口管理和DecorView頂層視圖的紐帶。View的繪制流程從ViewRootImpl的performTraversals方法開始,順序執(zhí)行measure、layout、draw這三個(gè)流程,最終完成對(duì)View的繪制工作,在performTraversals方法中,會(huì)調(diào)用measure、layout、draw這三個(gè)方法,這三個(gè)方法內(nèi)部也會(huì)調(diào)用其對(duì)應(yīng)的onMeasure、onLayout、onDraw方法,通常我們?cè)谧远xView時(shí),也就是重寫的這三個(gè)方法來實(shí)現(xiàn)View的具體繪制邏輯下面詳細(xì)了解下各個(gè)步驟經(jīng)歷的主要方法(這里貼的源碼版本為API23)一、measure在performTraversals方法中,第一個(gè)需要進(jìn)行的就是measure過程,獲取到必要信息后,performTraversals方法中首先會(huì)調(diào)用measureHierarchy方法,接著measureHierarchy方法里再去調(diào)用performMeasure方法,在performMeasure方法中最終就會(huì)去調(diào)用View的measure方法,從而開始進(jìn)行測(cè)量過程mView其實(shí)指的就是DecorView頂層視圖,從源碼可以看出,measure的遞歸過程就是從DecorView開始的View和ViewGroup的測(cè)量方法有一定區(qū)別,View通過measure方法就可以完成自身的測(cè)量過程,而ViewGroup不僅需要調(diào)用measure方法測(cè)量自己,還需要去遍歷其子元素的measure方法,其子元素如果是ViewGroup,則該子元素需使用同樣的方法再次遞歸下去。View來看看View是如何測(cè)量自己的寬高的先在View源碼中找到measure方法View的measure過程就是通過measure方法來完成,View中的measure方法是由ViewGroup的measureChild方法調(diào)用的,ViewGroup在調(diào)用該子View的measure方法的同時(shí)還傳入了子View的widthMeasureSpec和heightMeasureSpec值。該方法被定義為final類型,也就是說其measure過程是固定的,在measure中調(diào)用了onMeasure方法,如果想要自定義測(cè)量過程的話,需要重寫onMeasure方法。Google在介紹該方法的時(shí)候也說了Measuretheviewanditscontenttodeterminethemeasuredwidthandthemeasuredheight.Thismethodisinvokedby{@link#measure(int,int)}andshouldbeoverriddenbysubclassestoprovideaccurateandefficientmeasurementoftheircontents.該方法需要被子類覆蓋,讓子類提供精準(zhǔn)、有效的測(cè)量數(shù)據(jù),所以我們一般在進(jìn)行自定義View開發(fā)時(shí),需要自定義測(cè)量過程就需要復(fù)寫此方法。setMeasuredDimension方法的作用就是設(shè)置View的測(cè)量寬高,其實(shí)我們?cè)谑褂胓etMeasuredWidth/getMeasuredHeight方法獲取的寬高值就是此處設(shè)置的值。如果不復(fù)寫此onMeasure方法,則默認(rèn)使用getDefaultSize方法得到的值??梢园l(fā)現(xiàn),傳入的measureSpec數(shù)值被MeasureSpec解析成了對(duì)應(yīng)的數(shù)據(jù),這里簡(jiǎn)單介紹下MeasureSpec,它的作用就是告訴View應(yīng)該以哪一種模式測(cè)量這個(gè)View,SpecMode有三種模式:?UNSPECIFIED:表示父容器不對(duì)View有任何限制,這種模式主要用于系統(tǒng)內(nèi)部多次Measure的情況,不需要過多關(guān)注?AT_MOST:父容器已經(jīng)指定了大小,View的大小不能大于這個(gè)值,相當(dāng)于布局中使用的wrap_content模式?EXACTLY:表示View已經(jīng)定義了精確的大小,使用這個(gè)指定的精確大小specSize作為該View的大小,相當(dāng)于布局中我們指定了66dp這種精確數(shù)值或者match_parent模式傳入的measureSpec值經(jīng)過MeasureSpec.getMode方法獲取它的測(cè)量模式,MeasureSpec.getSize方法獲取對(duì)應(yīng)模式下的規(guī)格大小,從而確定了其最終的測(cè)量大小。ViewGroupViewGroup是一個(gè)繼承至View的抽象類,ViewGroup沒有實(shí)現(xiàn)測(cè)量自己的具體過程,因?yàn)槠溥^程是需要各個(gè)子類根據(jù)自己的需要再具體實(shí)現(xiàn),比如LinearLayout、RelativeLayout等布局的特性都是不同的,不能統(tǒng)一的去管理,所以就交給其子類自己去實(shí)現(xiàn)ViewGroup在measure時(shí),除了實(shí)現(xiàn)自身的測(cè)量,還需要對(duì)它的每個(gè)子元素進(jìn)行measure,在ViewGroup內(nèi)部提供了一個(gè)measureChildren的方法其中,mChilderenCount指的是該ViewGroup所擁有的子元素的個(gè)數(shù),通過一個(gè)for循環(huán)調(diào)用measureChild方法來測(cè)量其所有子元素該方法先通過child.getLayoutParams方法取得子元素的LayoutParams,然后調(diào)用getChildMeasureSpec方法計(jì)算出該子元素正確的MeasureSpec,再使用child.measure方法把這個(gè)MeasureSpec傳遞給View進(jìn)行測(cè)量。通過這一系列過程,就能讓各個(gè)子元素依次進(jìn)入measure了二、layout通過之前的measure過程,View已經(jīng)測(cè)量出了自己需要的寬高大小,performTraversals方法接下來就會(huì)執(zhí)行l(wèi)ayout過程host.layout(0,0,host.getMeasuredWidth(),host.getMeasuredHeight());layout的過程主要是用來確定View的四個(gè)頂點(diǎn)所在屏幕上的位置layout過程首先從View中的layout方法開始layout(intl,intt,intr,intb)方法里的四個(gè)參數(shù)分別指的是左、上、右、下的位置,這四個(gè)值是通過ViewRootImpl類里的performTraversals方法傳入的layout方法用來確定View自身的位置,mLeft、mTop、mBottom、mRight的值最終會(huì)由setOpticalFrame和setFrame方法確定,其實(shí)setOpticalFrame內(nèi)部最后也是通過調(diào)用setFrame方法設(shè)置的確定完View的四個(gè)頂點(diǎn)位置后,就相當(dāng)于View在父容器中的位置被確定了,接下來會(huì)調(diào)用onLayout方法,這個(gè)方法是沒有具體實(shí)現(xiàn)的和ViewGroup的onMeasure類似,onLayout方法的具體實(shí)現(xiàn)也是需要根據(jù)各個(gè)View或ViewGroup的特性來決定的,所以源碼中是個(gè)空方法,有興趣的可以去看看LinearLayout、RelativeLayout等實(shí)現(xiàn)了onLayout方法的ViewGroup子類之前的measure過程,得到的是測(cè)量寬高,而通過onLayout方法,進(jìn)一步確定了View的最終寬高,一般情況下,measure過程的測(cè)量寬高和layout過程確定的最終寬高是一樣的三、draw經(jīng)過以上步驟,View已經(jīng)確定好了大小和屏幕中顯示的位置,接著就可以繪制自身需要顯示的內(nèi)容了在performTraversals方法中,會(huì)調(diào)用performDraw方法,performDraw方法中調(diào)用draw方法,draw方法中接著調(diào)用drawSoftware方法首先會(huì)通過lockCanvas方法取得一個(gè)Canvas畫布對(duì)象,接著由mView(DecorView)頂層視圖去調(diào)用View的draw方法,并傳入一個(gè)Canvas畫布對(duì)象其實(shí)Google的工程師已經(jīng)把draw的繪制過程注釋的非常詳細(xì)了Drawtraversalperformsseveraldrawingstepswhichmustbeexecutedintheappropriateorder:1.Drawthebackground
2.Ifnecessary,savethecanvas'layerstoprepareforfading
3.Drawview'scontent
4.Drawchildren
5.Ifnecessary,drawthefadingedgesandrestorelayers
6.Drawdecorations(scrollbarsforinstance)1.繪制View的背景如果該View設(shè)置了背景,則繪制背景。此背景指的是我們?cè)诓季治募型ㄟ^android:background屬性,或代碼中使用setBackgroundResource、setBackgroundColor等方法設(shè)置的背景圖片或背景顏色dirtyOpaque屬性用來判斷該View是否是透明的,如果是透明的則不執(zhí)行某些步驟,比如繪制背景,繪制內(nèi)容等2.如果有必要的話,保存這個(gè)canvas畫布,為該層邊緣的fading效果作準(zhǔn)備第2步和第5步是配套的,我們一般不用管2和5,源碼中的注釋也說了,其中的2和5方法在通常情況下是直接跳過的(skipstep2&5ifpossible(commoncase)),其主要作用是實(shí)現(xiàn)一些如同View滑動(dòng)到邊緣時(shí)產(chǎn)生的陰影效果,可以不用過多關(guān)注3.繪制View的內(nèi)容該步驟調(diào)用了onDraw方法,這個(gè)方法是一個(gè)空實(shí)現(xiàn)每個(gè)子View需要展示的內(nèi)容肯定是不相同的,所以onDraw的詳細(xì)過程需要子類自己去實(shí)現(xiàn)4.繪制子View和第3步一樣,此方法也是一個(gè)空實(shí)現(xiàn)對(duì)于單純的View來說,它是沒有子View的,所以不需要實(shí)現(xiàn)該方法,該方法主要是被ViewGroup重寫了,找到ViewGroup中重寫的dispatchDraw方法在ViewGroup的dispatchDraw方法中通過for循環(huán)調(diào)用drawChild方法drawChild方法里調(diào)用子視圖的draw方法,從而讓其子視圖進(jìn)入draw過程5.繪制View邊緣的漸變褪色效果,類似于陰影效果當(dāng)?shù)?個(gè)步驟保存了canvas畫布后,就可以為這個(gè)畫布實(shí)現(xiàn)陰影效果6.繪制View的裝飾物
View的裝飾物,指的是View除了背景、內(nèi)容、子View的其它部分,比如滾動(dòng)條這些四、常見問題1.在Activity中獲取View的寬高,得到的值為0通過上面的measure分析可以知道,View的measure過程和Activity的生命周期方法不是同步的,所以無法保證Activity的某個(gè)生命周期執(zhí)行后View就一定能獲取到值,當(dāng)我們?cè)赩iew還沒有完成measure過程就去獲取它的寬高,當(dāng)然獲取不到了,解決這問題的方法有很多,這里推薦使用以下方法(1)在View的post方法中獲取:這個(gè)方法簡(jiǎn)單快捷,推薦使用post方法中傳入的Runnable對(duì)象將會(huì)在View的measure、layout過程后觸發(fā),因?yàn)閁I的事件隊(duì)列是按順序執(zhí)行的,所以任何post到隊(duì)列中的請(qǐng)求都會(huì)在Layout發(fā)生變化后執(zhí)行。(2)使用View的觀察者ViewTreeObserverViewTreeObserver是視圖樹的觀察者,其中OnGlobalLayoutListener監(jiān)聽的是一個(gè)視圖樹中布局發(fā)生改變或某個(gè)視圖的可視狀態(tài)發(fā)生改變時(shí),就會(huì)觸發(fā)此類監(jiān)聽事件,其中onGlobalLayout回調(diào)方法會(huì)在View完成layout過程后調(diào)用,此時(shí)是獲取View寬高的好時(shí)機(jī)使用這個(gè)方法需要注意,隨著View樹的狀態(tài)改變,onGlobalLayout方法會(huì)被回調(diào)多次,所以在進(jìn)入onGlobalLayout回調(diào)方法時(shí),就移除這個(gè)觀察者,保證onGlobalLayou
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年新疆全國(guó)客運(yùn)資格證模擬考試
- 2024年西安客運(yùn)基礎(chǔ)知識(shí)
- 2024年岳陽(yáng)申請(qǐng)客運(yùn)從業(yè)資格證版試題
- 2024年福建客運(yùn)駕駛從業(yè)資格證模擬考試題庫(kù)
- 2024年安康客運(yùn)從業(yè)資格證到期換證考試
- 藥物警戒偏差管理規(guī)程
- 通信原理信號(hào)源實(shí)驗(yàn)報(bào)告(共五篇)
- 全省技工院校職業(yè)技能大賽技術(shù)文件-礦井測(cè)風(fēng)技術(shù)文件(高級(jí)組)
- Magotan B8L 車身內(nèi)部維修
- 醫(yī)院捐贈(zèng)資產(chǎn)公示準(zhǔn)則
- 2024化糞池清理協(xié)議書
- 35kv集電線路監(jiān)理標(biāo)準(zhǔn)細(xì)則
- 橋式起重機(jī)定期檢查記錄表
- 2024年互聯(lián)網(wǎng)營(yíng)銷師(中級(jí))理論考試題庫(kù)(附答案)
- 冶金工程職業(yè)生涯規(guī)劃
- T∕CACM 1090-2018 中醫(yī)治未病技術(shù)操作規(guī)范 穴位敷貼
- 2024版人教版英語(yǔ)初一上單詞默寫單
- 醫(yī)療衛(wèi)生機(jī)構(gòu)反恐
- 數(shù)據(jù)中心儲(chǔ)能白皮書
- 化學(xué)實(shí)驗(yàn)室安全智慧樹知到期末考試答案2024年
評(píng)論
0/150
提交評(píng)論