Android自定義View(三、深入解析控件測量onMeasure)_第1頁
Android自定義View(三、深入解析控件測量onMeasure)_第2頁
Android自定義View(三、深入解析控件測量onMeasure)_第3頁
Android自定義View(三、深入解析控件測量onMeasure)_第4頁
Android自定義View(三、深入解析控件測量onMeasure)_第5頁
已閱讀5頁,還剩4頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Android自定義View(三、深入解析控件測量onMeasure)1. onMeasure什么時候會被調用onMeasure方法的作用時測量空間的大小,什么時候需要測量控件的大小呢?我們舉個栗子,做飯的時候我們炒一碗菜,炒菜的過程我們并不要求知道這道菜有多少分量,只有在菜做熟了我們要拿個碗盛放的時候,我們才需要掂量拿多大的碗盛放,這時候我們就要對菜的分量進行估測。 而我們的控件也正是如此,創建一個View(執行構造方法)的時候不需要測量控件的大小,只有將這個view放入一個容器(父控件)中的時候才需要測量,而這個測量方法就是父控件喚起調用的。當控件的父控件要放置該控件的時候,父控件會調用子

2、控件的onMeasure方法詢問子控件:“你有多大的尺寸,我要給你多大的地方才能容納你?”,然后傳入兩個參數(widthMeasureSpec和heightMeasureSpec),這兩個參數就是父控件告訴子控件可獲得的空間以及關于這個空間的約束條件(好比我在思考需要多大的碗盛菜的時候我要看一下碗柜里最大的碗有多大,菜的分量不能超過這個容積,這就是碗對菜的約束),子控件拿著這些條件就能正確的測量自身的寬高了。 2. onMeasure方法執行流程上面說到onMeasure方法是由父控件調用的,所有父控件都是ViewGroup的子類,ViewGroup是一個抽象類,它里面有一個抽象方法onLay

3、out,這個方法的作用就是擺放它所有的子控件(安排位置),因為是抽象類,不能直接new對象,所以我們在布局文件中可以使用View但是不能直接使用 ViewGroup。在給子控件確定位置之前,必須要獲取到子控件的大小(只有確定了子控件的大小才能正確的確定上下左右四個點的坐標),而ViewGroup并沒有重寫View的onMeasure方法,也就是說抽象類ViewGroup沒有為子控件測量大小的能力,它只能測量自己的大小。但是既然ViewGroup是一個能容納子控件的容器,系統當然也考慮到測量子控件的問題,所以ViewGroup提供了三個測量子控件相關的方法(measuireChildrenmea

4、suireChildmeasureChildWithMargins),只是在ViewGroup中沒有調用它們,所以它本身不具備為子控件測量大小的能力,但是他有這個潛力哦。為什么都有測量子控件的方法了而ViewGroup中不直接重寫onMeasure方法,然后在onMeasure中調用呢?因為不同的容器擺放子控件的方式不同,比如RelativeLayout,LinearLayout這兩個ViewGroup的子類,它們擺放子控件的方式不同,有的是線性擺放,而有的是疊加擺放,這就導致測量子控件的方式會有所差別,所以ViewGroup就干脆不直接測量子控件,他的子類要測量子控件就根據自己的布局特性重寫

5、onMeasure方法去測量。這么看來ViewGroup提供的三個測量子控件的方法豈不是沒有作用?答案是NO,既然提供了就肯定有作用,這三個方法只是按照一種通用的方式去測量子控件,很多ViewGruop的子類測量子控件的時候就使用了ViewGroup的measureChildxxx系列方法;還有一個作用就是為我們自定義ViewGroup提供方便咯,自定義ViewGroup我會在以后的博客中專門探討,這里就不大費篇章了。測量的時候父控件的onMeasure方法會遍歷他所有的子控件,挨個調用子控件的measure方法,measure方法會調用onMeasure,然后會調用setMeasureDim

6、ension方法保存測量的大小,一次遍歷下來,第一個子控件以及這個子控件中的所有子控件都會完成測量工作;然后開始測量第二個子控件;最后父控件所有的子控件都完成測量以后會調用setMeasureDimension方法保存自己的測量大小。值得注意的是,這個過程不只執行一次,也就是說有可能重復執行,因為有的時候,一輪測量下來,父控件發現某一個子控件的尺寸不符合要求,就會重新測量一遍。舉個栗子,看下圖:3. MeasureSpec類上面說到MeasureSpec約束是由父控件傳遞給子控件的,這個類里面到底封裝了什么東西?我們看一看源碼:public static class MeasureSpec p

7、rivate static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /* * 父控件不強加任何約束給子控件,它可以是它想要任何大小 */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /* * 父控件已為子控件確定了一個確切的大小,孩子將被給予這些界限,不管子控件自己希望的是多大 */ public static final int EXACTLY = 1 << MODE

8、_SHIFT; /* * 父控件會給子控件盡可能大的尺寸 */ public static final int AT_MOST = 2 << MODE_SHIFT; /* * 根據所提供的大小和模式創建一個測量規范 */ public static int makeMeasureSpec(int size, int mode) if (sUseBrokenMakeMeasureSpec) return size + mode; else return (size & MODE_MASK) | (mode & MODE_MASK); /* * 從所提供的測量規范中提取

9、模式 */ public static int getMode(int measureSpec) return (measureSpec & MODE_MASK); /* * 從所提供的測量規范中提取尺寸 */ public static int getSize(int measureSpec) return (measureSpec & MODE_MASK); .從源碼中我們知道,MeasureSpec其實就是尺寸和模式通過各種位運算計算出的一個整型值,它提供了三種模式,還有三個方法(合成約束、分離模式、分離尺寸)。4. 從ViewGroup的onMeasure到View的o

10、nMeasure. ViewGroup中三個測量子控件的方法:通過上面的介紹,我們知道,如果要自定義ViewGroup就必須重寫onMeasure方法,在這里測量子控件的尺寸。子控件的尺寸怎么測量呢?ViewGroup中提供了三個關于測量子控件的方法: /* *遍歷ViewGroup中所有的子控件,調用measuireChild測量寬高 */ protected void measureChildren (int widthMeasureSpec, int heightMeasureSpec) final int size = mChildrenCount; final View childr

11、en = mChildren; for (int i = 0; i < size; +i) final View child = childreni; if (child.mViewFlags & VISIBILITY_MASK) != GONE) /測量某一個子控件寬高 measureChild(child, widthMeasureSpec, heightMeasureSpec); /* 測量某一個child的寬高*/protected void measureChild (View child, int parentWidthMeasureSpec, int parentH

12、eightMeasureSpec) final LayoutParams lp = child.getLayoutParams(); /獲取子控件的寬高約束規則 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp. width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPadd

13、ingBottom, lp. height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);/* 測量某一個child的寬高,考慮margin值*/protected void measureChildWithMargins (View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) final MarginLayoutParams lp = (MarginLayoutPara

14、ms) child.getLayoutParams(); /獲取子控件的寬高約束規則 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp. leftMargin + lp.rightMargin + widthUsed, lp. th); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop +

15、mPaddingBottom + lp. topMargin + lp.bottomMargin + heightUsed, lp. height); /測量子控件 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);這三個方法分別做了那些工作大家應該比較清楚了吧?measureChildren 就是遍歷所有子控件挨個測量,最終測量子控件的方法就是measureChild 和measureChildWithMargins 了,我們先了解幾個知識點:measureChildWithMargins跟measureChild的區別

16、就是父控件支不支持margin屬性支不支持margin屬性對子控件的測量是有影響的,比如我們的屏幕是1080x1920的,子控件的寬度為填充父窗體,如果使用了marginLeft并設置值為100; 在測量子控件的時候,如果用measureChild,計算的寬度是1080,而如果是使用measureChildWithMargins,計算的寬度是1080-100 = 980。怎樣讓ViewGroup支持margin屬性?ViewGroup中有兩個內部類ViewGroup.LayoutParams和ViewGroup. MarginLayoutParams,MarginLayoutParams繼承自

17、LayoutParams ,這兩個內部類就是VIewGroup的布局參數類,比如我們在LinearLayout等布局中使用的layout_widthlayout_hight等以“layout_ ”開頭的屬性都是布局屬性。在View中有一個mLayoutParams的變量用來保存這個View的所有布局屬性。LayoutParams和MarginLayoutParams 的關系: LayoutParams 中定義了兩個屬性(現在知道我們用的layout_widthlayout_hight的來頭了吧?):<declare-styleable name= "ViewGroup_Layo

18、ut"> <attr name ="layout_width" format="dimension"> <enum name ="fill_parent" value="-1" /> <enum name ="match_parent" value="-1" /> <enum name ="wrap_content" value="-2" /> </attr >

19、 <attr name ="layout_height" format="dimension"> <enum name ="fill_parent" value="-1" /> <enum name ="match_parent" value="-1" /> <enum name ="wrap_content" value="-2" /> </tr ></declare-

20、styleable >MarginLayoutParams 是LayoutParams的子類,它當然也延續了layout_widthlayout_hight 屬性,但是它擴充了其他屬性:< declare-styleable name ="ViewGroup_MarginLayout"> <attr name ="layout_width" /> <!-使用已經定義過的屬性-> <attr name ="layout_height" /> <attr name ="

21、layout_margin" format="dimension" /> <attr name ="layout_marginLeft" format= "dimension" /> <attr name ="layout_marginTop" format= "dimension" /> <attr name ="layout_marginRight" format= "dimension" /> &l

22、t;attr name ="layout_marginBottom" format= "dimension" /> <attr name ="layout_marginStart" format= "dimension" /> <attr name ="layout_marginEnd" format= "dimension" /></declare-styleable >是不是對布局屬性有了一個全新的認識?原來我們使用的margin

23、屬性是這么來的。為什么LayoutParams 類要定義在ViewGroup中? 大家都知道ViewGroup是所有容器的基類,一個控件需要被包裹在一個容器中,這個容器必須提供一種規則控制子控件的擺放,比如你的寬高是多少,距離那個位置多遠等。所以ViewGroup有義務提供一個布局屬性類,用于控制子控件的布局屬性。為什么View中會有一個mLayoutParams 變量? 我們在之前學習自定義控件的時候學過自定義屬性,我們在構造方法中,初始化布局文件中的屬性值,我們姑且把屬性分為兩種。一種是本View的繪制屬性,比如TextView的文本、文字顏色、背景等,這些屬性是跟View的繪制相關的。另

24、一種就是以“layout_”打頭的叫做布局屬性,這些屬性是父控件對子控件的大小及位置的一些描述屬性,這些屬性在父控件擺放它的時候會使用到,所以先保存起來,而這些屬性都是ViewGroup.LayoutParams定義的,所以用一個變量保存著。怎樣讓ViewGroup支持margin屬性? 這一部分知識點我們在下一篇博客自定義ViewGroup中再去講解 . getChildMeasureSpec方法measureChildWithMargins跟measureChild 都調用了這個方法,其作用就是通過父控件的寬高約束規則和父控件加在子控件上的寬高布局參數生成一個子控件的約束。我們知道View

25、的onMeasure方法需要兩個參數(父控件對View的寬高約束),這個寬高約束就是通過這個方法生成的。有人會問為什么不直接拿著子控件的寬高參數去測量子控件呢?打個比方,父控件的寬高約束為wrap_content,而子控件為match_perent,是不是很有意思,父控件說我的寬高就是包裹我的子控件,我的子控件多大我就多大,而子控件說我的寬高填充父窗體,父控件多大我就多大。最后該怎么確定大小呢?所以我們需要為子控件重新生成一個新的約束規則。只要記住,子控件的寬高約束規則是父控件調用getChildMeasureSpec方法生成。getChildMeasure方法代碼不多,也比較簡單,就是幾個s

26、witch將各種情況考慮后生成一個子控件的新的寬高約束,這個方法的結果能夠用一個表來概括:進行了上面的步驟,接下來就是在measureChildWithMarginsh或者measureChild中 調用子控件的measure方法測量子控件的尺寸了。. View的onMeasureView中onMeasure方法已經默認為我們的控件測量了寬高,我們看看它做了什么工作:protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) setMeasuredDimension( getDefaultSize(getSugg

27、estedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);/* * 為寬度獲取一個建議最小值 */protected int getSuggestedMinimumWidth () return (mBackground = null) ? mMinWidth : max(mMinWidth , mBackground.getMinimumWidth();/* * 獲取默認的寬高值 */public static int getDefaultSize (int size, int measureSpec) int result = size; int specMode = MeasureSpec. getMode(measureSpec); int specSize = MeasureSpec. getSize(measureSpec); switch (specMode) case MeasureSpec. UNSPECIFIED: result = size; break; case MeasureSpec. AT_MOST: case M

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論