天天看點

Android View原理measure(計算視圖大小)layout(計算視圖位置)draw(繪制視圖)

View視圖繪制基本操作由三個函數完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三個子方法。那我們具體來看一下。

measure(計算視圖大小)

視圖大小,準确應該是指視圖的布局大小。我們在調用的時候應該注意以下幾點:

  1. measure()該方法被修飾為final,不允許重載,View子類隻能通過重載onMeasure()來完善自己的測量邏輯。
  2. measure(int widthMeasureSpec, int heightMeasureSpec)

    ,MeasureSpec作為輸入參數,該值為int型,其值由兩部分組成是一個32位的int型的數值,高2位代表規格模式specMode,低30位代表具體尺寸specSize。其中specMode有三種模式:
    • MeasureSpec.EXACTLY:确定模式,即父視圖希望子視圖的大小是确定的,由specSize決定;如200dp,match_parent。
    • MeasureSpec.AT_MOST:最多模式,即父視圖希望子視圖的大小最多是specSize指定的值;如wrap_content。
    • MeasureSpec.UNSPECIFIED:未指定模式,此時父視圖完全尊重子視圖的設計;系統
  3. 最頂層視圖DecorView測量時的MeasureSpec從何而來呢,是在ViewRootImpl中調用getRootMeasureSpec()獲得,LayoutParam寬高參數均為MATCH_PARENT。
  4. 視圖的大小由父視圖和子視圖共同決定,比如LinearLayout的layout_height設定為wrap_content,視圖大小由子視圖決定;設定為0dp,視圖大小的高度就為0。
  5. 根據子視圖自己的測量規格調用child.measure()進行最終的測量;

    layout(計算視圖位置)

計算視圖位置也可以說成是布局,視圖的布局過程是從ViewRoot對象調調用根視圖的layout()方法開始,接着layout()方法調用根視圖的onLayout()方法,onLayout()方法會對所包含的子視圖逐一執行layout操作,如果子視圖是ViewGroup子類對象,則繼續調用ViewGroup子視圖的layout(),重複這一過程。如果子視圖是View子類對象,則在子視圖重載的onLayout()内部将自己布局到視圖中,不需要對子視圖進行layout操作,這樣一次layout過程結束。我們來看看layout的調用流程:

1.首先調用layout(),如果繼承View,layout()方法可被重載,如果繼承ViewGroup,layout()修飾為final,不可重載;ViewGroup.onLayout()修飾為abstract,子類必須重載。我們來看看

onLayout(boolean changed, int l, int t, int r, int b)

l,t,r,b分别表示什麼呢?讓下面這張圖來告訴我們。

Android View原理measure(計算視圖大小)layout(計算視圖位置)draw(繪制視圖)

l,t,r,b分别對應的藍色的getLeft(),getTop(),getRight(),getBottom。

2.調用setFrame(l,t,r,b)會将位置資訊儲存起來,這些參數将會儲存到View的内部變量 (mLeft、mTop、mRight、mBottom)中。在儲存完變量前,會先對比這些參數是否和原來的相同,如果相同,則什麼都不做,如果不同則進行重新指派,并在指派前給mPrivateFlags中添加DRAWN辨別,同時調用invalidate()通知View系統原來占用的位置需要重繪。

3.調用onLayout(),View中定義的onLayout()方法預設什麼都不做,View系統提供onLayout()方法的目的是為了使系統包含的子視圖的父視圖能夠在onLayout()方法對子視圖進行位置配置設定,正因為如此,如果是父視圖,則必須重寫onLayout(),也正因為如此ViewGroup類才會把onLayout重載改成了abstract類型。

4.清除mPrivateFlags中的LAYOUT_REQUIRED辨別,因為layout操作已經完成。

draw(繪制視圖)

繪制過程就是把View對象繪制到螢幕上,如果該View是一個容器ViewGroup,則需要遞歸繪制其所包含的所有子視圖;繪制的流程如下:

  1. View背景Backgroud:每個視圖都可以有一個背景,背景可以是一個顔色值,也可以是一副圖檔,甚至可以是任何Drawable對象;
  2. 視圖自身的内容:一般由視圖設計者在視圖的onDraw方法中完成具體的内容繪制,比如TextView的内容就是具體的文字,若是視圖容器ViewGroup,則需要遞歸完成子視圖的具體内容繪制;
  3. 漸變邊框FadingEdge:其作用是為了讓視圖的邊框看起來更有層次感,其本質就是一個Shader對象,當然可以通過配置項關閉該效果;
  4. 滾動條ScrollBar:用來顯示目前滾動的位置和狀态;