android繪製view的過程

2021-09-22 17:17:33 字數 4692 閱讀 4846

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的大概工作過程,當然了,只是概述,細節多成馬了!!!!! 

2 measure的過程

public final void measure(int widthmeasurespec, int heightmeasurespec)的引數**及代表的意思 

這個兩個引數都是有父view傳遞過來的,也就是代表了父view的大小。其實說大小不太對,應該說是建議「規格」。他有兩部分組成,

第一部分:高16位表示mode,定義在measurespec類中,有三種型別,

(1) measurespec.exactly:表示確定大小,父元素決定子元素的確切大小 ,子元素將被限定在給定的邊界裡而忽略它本身大小

(2) measurespec.at_most:表示最大大小,子元素至多達到指定大小的值。

(3) 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裡邊做什麼了 .

measurespec類怎麼使用?

通常在view元件的onmeasure方法裡面呼叫.

它常用的三個函式:

1.static int getmode(int measurespec):根據提供的測量值(格式)提取模式(上述三個模式之一)

2.static int getsize(int measurespec):根據提供的測量值(格式)提取大小值(這個大小也就是我們通常所說的大小)

3.static int makemeasurespec(int size,int mode):根據提供的大小值和模式建立乙個測量值(格式)

3 .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的總和相等,否則平分可能不能得到預期效果,分析乙個例子吧:   

上面這個例子再計算第乙個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時候使用, 

Android中View繪製優化

1.優化布局層次 2.使用標籤復用布局檔案 includelayout layout titlebar 關於標籤的第乙個比較簡單的用法 關於使用標籤的一些限制 1 它只能作為xml布局宣告的root元素來使用 2 使用它來inflate乙個布局時,必須指定乙個viewgroup例項作為其父元素並且設...

Android 獲取View繪製前的高度

在android開發過程中,我們可能需要獲取view繪製前的高度或者寬度,一種的可能情形是我們初始化的時候讓某個view是visible gone的,當我們觸發某個事件的時候需要它顯示並且希望有一些動畫效果。這時候我們就要獲取這個view顯示前即繪製前的寬度或者高度。原理很簡單,我們知道,view的...

Android 高階之 View 的繪製 三

原始碼分析 layout 作用 確定view本身的位置,即設定view本身的四個頂點位置 public void layout int l,int t,int r,int b 分析1 setframe 作用 根據傳入的4個位置值,設定view本身的四個頂點位置 即 最終確定view本身的位置 pro...