天天看點

android繪制view的過程之一---------計算view大小(measure)(轉)

http://www.eoeandroid.com/forum.php?mod=viewthread&tid=200062&extra=page%3D1&page=1

1.android繪制view的過程簡單描述

簡單描述可以解釋為:計算大小(measure),布局坐标計算(layout),繪制到螢幕(draw);

下面看看每一步的動作到底是什麼,

第一步:當activity啟動的時候,觸發初始化view過程的是由Window對象的DecorView調用View(具體怎樣從xml中讀取是用LayoutInflater.from(context).inflate)對象的 public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法開始的,這個方法是final類型的,也就是所有的子類都不能繼承該方法,保證android初始化view的原理不變。具體參數類值,後面會介紹。

第二步:View的measure方法 onMeasure(widthMeasureSpec, heightMeasureSpec),該方法進行實質性的view大小計算。注意:view的大小是有父view和自己的大小決定的,而不是單一決定的。這也就是為什麼ViewGroup的子類會重新該方法,比如LinearLayout等。因為他們要計算自己和子view的大小。View基類有自己的實作,隻是設定大小。其實根據源碼來看,measure的過程本質上就是把Match_parent和wrap_content轉換為實際大小

第三步:當measure結束時,回到DecorView,計算大小計算好了,那麼就開始布局了,開始調用view的 public final void layout(int l, int t, int r, int b),該還是也是final類型的,目的和measure方法一樣。layout方法内部會調用onlayout(int l, int t, int r, int b )方法,二ViewGroup将此方法abstract的了,是以我們繼承ViewGroup的時候,需要重新該方法。該方法的本質是通過measure計算好的大小,計算出view在螢幕上的坐标點

第四步:measure過了,layout過了,那麼就要開始繪制到螢幕上了,是以開始調用view的 public void draw(Canvas canvas)方法,此時方法不是final了,原因是程式員可以自己畫,内部會調用ondraw,我們經常需要重寫的方法。

以上就是view的大概工作過程,當然了,隻是概述,細節多成馬了!!!!!

這次主要是跟分析一下measure的過程。下面進入今日主題! ---------------measure的過程!

1. public final void measure(int widthMeasureSpec, int heightMeasureSpec)的參數來源及代表的意思

這個兩個參數都是有父view傳遞過來的,也就是代表了父view的大小。其實說大小不太對,應該說是建議“規格”。他有兩部分組成,第一部分:高16位表示MODE,定義在MeasureSpec類中,有三種類型,MeasureSpec.EXACTLY:表示确定大小, MeasureSpec.AT_MOST:表示最大大小, MeasureSpec.UNSPECIFIED:不确定。第二部分:低16位表示size,既父view的大小,這就是為什麼,我們在重寫onmeasure方法是需要:int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec);這樣調用,因為MeasureSpec知道怎麼讀取。對于跟view(并不是我們在xml中聲明的第一個元素),而是系統的Window對象的decorVIew對象。Mode一般都為MeasureSpec.EXACTLY ,而size分别對應螢幕寬,高。也就是Window第一次掉用的view(這個view才是Xml中聲明的第一個元素),一般都是這個值,而對于子view來說,這連個值就是你在xml定義的屬性 android:layout_width和android:layout_height這個,當然了上面說過view的大小是有父view和子view共同決定的,是以這樣有點不對,但是來源于這兩個值。意思明白了,我們看看measure裡邊做什麼了

2.measure方法内部操作過程

•調用 onMeasure(widthMeasureSpec, heightMeasureSpec),将父view的建議大小和規格傳入,view類自己的onmeasure方法,隻是根據xml中配置的大小初始化大小 ,這個就不分析了,重要的我們看看viewGroup中的onMeasure方法,ViewGroup類中沒有處理該方法,一般在他的子類中處理,比如LinearLayout中,我們以Linearlayout作為分析類。

•Linearlayout類的onMeasure方法分兩種情況處理,1:重置排序,2:水準排序,這個大家都知道,我們分析重置排序,

•擷取所有的子view數量,對每個子view開始處理,如果子view是GONE的,則直接跳過

•在LinearLaout.measureVertical方法中,首先:擷取該子view的LayoutParams,在xml中定義的參數,将通過layout_weight定義的值累加到變量totalWeight中,所有的權重,然後判斷如果view的height設定為零,但weight設定的大于0,則将height的值設定為LayoutParams.WRAP_CONTENT這個值,其他的不處理

•然後調用measureChildWithMargins方法,這個做的處理包括:計算子view的measureSpec,即MODE和SIZE,調用方法為:getChildMeasureSpec,調用兩次,分别 計算寬和高,getChildMeasureSpec内部根據父view的Measure和子view的layout_width和layout_height屬性計算子view的measure,檢視圖檔:getChildMeasureSpec計算子view的measure,總結如下:1.如果在xml中指定了子view的具體大小,那麼計算結果不管父的measure是什麼,結果都是EXACITY+child_size,2.如果子view的height指定

的值為FILL_PARENT,則傳回的結果為:EXACITY+size,原因很簡單:因為FILL_PARENT的意思是充滿這個父view,是以傳回的子view的measure就是view的大小,白!3.如果子vide的大小為wrap_content,那麼傳回的結果都為At_most+size,原因是:最大不能超過父view的大小。

•子view的measure确定好以後,然後調用子view的measure方法,如果子view是View對象,則該view的大小測量結束,開始下一個子view的循環,如果子view是ViewGroup那麼,又開始一個新的遞歸,處理邏輯和上面一樣,值得所有的view對象測量結束。

•LinearLayout會在每個他的直接子view測量結束後,将該子view的高度累加到變量mTotalLength,其其實應該叫mTotalHeight更合适,但是為了和wight同一,是以命名為這個(這個過程不處理:父view的大小指定為具體值和fill_parent,且子view的高度指定為0和子viewweight值大于的)。

•所有的子view測量結束後,才開始對layout_weight計算,這樣我們可能想到,如果父view已經被占滿了,那麼有可能layout_weight大于0的view對象是不會顯示的,而計算layout_weight的方法也很簡單,就是用總高度減去上面分析完mTotalLength的值,就是剩下,然後去平分給view對象,注意計算權重時優先去android:android:weightSum(LinearLayout的xml屬性)的值,如果不設定該值會計算和,是以該值既然設定了,就一定要子view的weight的總和相等,否則平分可能不能得到預期效果,分析一個例子吧:

<LinearLayout android:layout_width="fill_parent"

android:layout_height="200dp"

>

<TextView android:layout_width="fill_parent"

android:layout_height="100dp"

android:layout_weight="1"

/>

<TextView android:layout_width="fill_parent"

android:layout_height="0dp"

android:layout_weight="1"

/>

</LinearLayout>

上面這個例子再計算第一個TextView的時候,根據android:layout_height="100dp" 值,确定高度為100dp,計算第二個TextView的時候,由于android:layout_height="0"為0,是以不計算其高度,等到計算weight的時候才計算,當計算weight的時候,千萬别認為第一個TextView已經計算過了,就不計算了,還是計算的,計算過程如下:第一個配置設定了100dp,還剩100dp,是以兩個textview各分50dp,是以第一個TextVIew的 150dp,第二個就為50dp

至此,view的measure就結束了,所有的view值都結束了,大家可能發現,這個過程隻用了幾個屬性: android:layout_width,android:layout_height,android:layout_weight還有marger和pading,其他的多數屬性都在ondraw時候使用,

以上分析全部是自己看源碼分析的,有錯誤之處,歡迎各位指出,以免誤導他人。

關于布局(layout),ondraw後續有時間再補上,今天PM給配置設定任務,得開始幹活了,!

加入簡易流程圖:

daxiao.jpg (28.15 KB, 下載下傳次數: 16)

2012-9-14 12:54 上傳

點選檔案名下載下傳附件

父view的measure 子view大小(layout_widht和layout_height的值) 計算出子view的measure EXACITY+size chil ...

daxiao.jpg (28.15 KB, 下載下傳次數: 9)

2012-9-14 12:52 上傳

點選檔案名下載下傳附件

getChildMeasureSpec計算子view的measure結果

view_onmeasure流程圖.jpg (25.65 KB, 下載下傳次數: 8)

2012-9-14 15:19 上傳

點選檔案名下載下傳附件