您好,登錄后才能下訂單哦!
Android中MeasureSpec的作用是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
MeasureSpec的創(chuàng)建規(guī)則:
實(shí)例詳解:
package cc.ww; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.widget.LinearLayout; /** * @author http://blog.csdn.net/lfdfhl * * 文檔描述: * 關(guān)于MeasureSpec的理解 * * (1) MeasureSpec基礎(chǔ)知識 * MeasureSpec通常翻譯為"測量規(guī)格",它是一個32位的int數(shù)據(jù). * 其中高2位代表SpecMode即某種測量模式,低32位為SpecSize代表在該模式下的規(guī)格大小. * 可以通過: * int specMode = MeasureSpec.getMode(measureSpec) 獲取specMode int specSize = MeasureSpec.getSize(measureSpec) 獲取SpecSize 常用的SpecMode有三種: MeasureSpec.EXACTLY 官方文檔 Measure specification mode: The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be. 父容器已經(jīng)檢測出子View所需要的精確大小.該子View最終的測量大小即為SpecSize. (1) 當(dāng)子View的LayoutParams的寬(高)采用具體的值(如100px)時且父容器的MeasureSpec為 MeasureSpec.EXACTLY或者 MeasureSpec.AT_MOST或者M(jìn)easureSpec.UNSPECIFIED時: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.EXACTLY 系統(tǒng)返回給該子View的specSize就為子View自己指定的大小(childSize) 通俗地理解: 子View的LayoutParams的寬(高)采用具體的值(如100px)時,那么說明該子View的大小是非常明確的,明確到已經(jīng)用具體px值 指定的地步了.那么此時不管父容器的specMode是什么,系統(tǒng)返回給該子View的specMode總是MeasureSpec.EXACTLY,并且 系統(tǒng)返回給該子View的specSize就為子View自己指定的大小(childSize). (2) 當(dāng)子View的LayoutParams的寬(高)采用match_parent時并且父容器的MeasureSpec為 MeasureSpec.EXACTLY時: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.EXACTLY 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) 通俗地理解: 子View的LayoutParams的寬(高)采用match_parent時并且父容器的MeasureSpec為 MeasureSpec.EXACTLY. 這時候說明子View的大小還是挺明確的:就是要和父容器一樣大,更加直白地說就是父容器要怎樣子View就要怎樣. 所以,如果父容器MeasureSpec為 MeasureSpec.EXACTLY那么: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.EXACTLY,和父容器一樣. 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize),就是父容器的剩余大小. 同樣的道理如果此時,MeasureSpec為 MeasureSpec.AT_MOST呢? 系統(tǒng)返回給該子View的specMode也為 MeasureSpec.AT_MOST,和父容器一樣. 系統(tǒng)返回給該子View的specSize也為該父容器剩余空間的大小(parentLeftSize),就是父容器的剩余大小. MeasureSpec.AT_MOST 官方文檔 The child can be as large as it wants up to the specified size. 父容器指定了一個可用大小即specSize,子View的大小不能超過該值. (1) 當(dāng)子View的LayoutParams的寬(高)采用match_parent時并且父容器的MeasureSpec為 MeasureSpec.AT_MOST時: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.AT_MOST 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) 這種情況已經(jīng)在上面介紹 MeasureSpec.EXACTLY時已經(jīng)討論過了. (2) 當(dāng)子View的LayoutParams的寬(高)采用wrap_content時并且父容器的MeasureSpec為 MeasureSpec.EXACTLY時: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.AT_MOST 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) 通俗地理解: 子View的LayoutParams的寬(高)采用wrap_content時說明這個子View的寬高不明確,要視content而定. 這個時候如果父容器的MeasureSpec為 MeasureSpec.EXACTLY即父容器是一個精確模式;這個時候簡單地說 子View是不確定的,父容器是確定的,那么 系統(tǒng)返回給該子View的specMode也就是不確定的即為 MeasureSpec.AT_MOST 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) (3) 當(dāng)子View的LayoutParams的寬(高)采用wrap_content時并且父容器的MeasureSpec為 MeasureSpec.AT_MOST時: 系統(tǒng)返回給該子View的specMode就為 MeasureSpec.AT_MOST 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) 通俗地理解: 子View的LayoutParams的寬(高)采用wrap_content時說明這個子View的寬高不明確,要視content而定. 這個時候如果父容器的MeasureSpec為 MeasureSpec.AT_MOST這個時候簡單地說 子View是不確定的,父容器也是不確定的,那么 系統(tǒng)返回給該子View的specMode也就是不確定的即為 MeasureSpec.AT_MOST 系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize) MeasureSpec.UNSPECIFIED 官方文檔 The parent has not imposed any constraint on the child. It can be whatever size it wants. 父容器不對子View的大小做限制. 一般用作Android系統(tǒng)內(nèi)部,或者ListView和ScrollView.在此不做討論. 關(guān)于這個三種測量規(guī)格下面的源碼分析中體現(xiàn)得很明顯,也可參考以下附圖. * (2) 在onMeasure()時子View的MeasureSpec的形成過程分析 * 關(guān)于該技術(shù)點(diǎn)的討論,請看下面的源碼分析. * */ public class UnderstandMeasureSpec { /** * 第一步: * 在ViewGroup測量子View時會調(diào)用到measureChildWithMargins()方法,或者與之類似的方法. * 請注意方法的參數(shù): * @param child * 子View * @param parentWidthMeasureSpec * 父容器(比如LinearLayout)的寬的MeasureSpec * @param widthUsed * 父容器(比如LinearLayout)在水平方向已經(jīng)占用的空間大小 * @param parentHeightMeasureSpec * 父容器(比如LinearLayout)的高的MeasureSpec * @param heightUsed * 父容器(比如LinearLayout)在垂直方向已經(jīng)占用的空間大小 * * 在該方法中主要有四步操作,其中很重要的是調(diào)用了getChildMeasureSpec()方法來確定 * 子View的MeasureSpec.詳情參見代碼分析 */ protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { //1 得到子View的LayoutParams final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //2 得到子View的寬的MeasureSpec final int childWidthMeasureSpec = getChildMeasureSpec (parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); //3 得到子View的高的MeasureSpec final int childHeightMeasureSpec = getChildMeasureSpec (parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); //4 測量子View child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } /** * getChildMeasureSpec()方法確定子View的MeasureSpec * 請注意方法的參數(shù): * @param spec * 父容器(比如LinearLayout)的寬或高的MeasureSpec * @param padding * 父容器(比如LinearLayout)在垂直方向或者水平方向已被占用的空間. * 在measureChildWithMargins()方法里調(diào)用getChildMeasureSpec()時注意第二個參數(shù)的構(gòu)成: * 比如:mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin * 其中: * mPaddingLeft和mPaddingRight表示父容器左右兩內(nèi)側(cè)的padding * lp.leftMargin和lp.rightMargin表示子View左右兩外側(cè)的margin * 這四部分都不可以再利用起來布局子View.所以說這些值的和表示: * 父容器在水平方向已經(jīng)被占用的空間 * 同理: * mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin * 表示: * 父容器(比如LinearLayout)在垂直方向已被占用的空間. * @param childDimension * 通過子View的LayoutParams獲取到的子View的寬或高 * * * 經(jīng)過以上分析可從getChildMeasureSpec()方法的第一個參數(shù)和第二個參數(shù)可以得出一個結(jié)論: * 父容器(如LinearLayout)的MeasureSpec和子View的LayoutParams共同決定了子View的MeasureSpec!??! * * * */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { /** * 第一步:得到父容器的specMode和specSize */ int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); /** * 第二步:得到父容器在水平方向或垂直方向可用的最大空間值. * 關(guān)于padding參見上面的分析 */ int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; /** * 第三步:確定子View的specMode和specSize. * 在此分為三種情況進(jìn)行. */ switch (specMode) { /** * 第一種情況: * 父容器的測量模式為EXACTLY * * 請注意兩個系統(tǒng)常量: * LayoutParams.MATCH_PARENT=-1 * LayoutParams.WRAP_CONTENT=-2 * 所以在此處的代碼: * childDimension >= 0 表示子View的寬或高不是MATCH_PARENT和WRAP_CONTENT */ case MeasureSpec.EXACTLY: /** * 當(dāng)父容器的測量模式為EXACTLY時如果: * 子View的寬或高是一個精確的值,比如100px; * 那么: * 子View的size就是childDimension * 子View的mode也為MeasureSpec.EXACTLY */ if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; /** * 當(dāng)父容器的測量模式為EXACTLY時如果: * 子View的寬或高是LayoutParams.MATCH_PARENT * 那么: * 子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size * 子View的mode也為MeasureSpec.EXACTLY */ } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; /** * 當(dāng)父容器的測量模式為EXACTLY時如果: * 子View的寬或高是LayoutParams.WRAP_CONTENT * 那么: * 子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size * 子View的mode為MeasureSpec.AT_MOST */ } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; /** * 第二種情況: * 父容器的測量模式為AT_MOST * * 請注意兩個系統(tǒng)常量:pp * LayoutParams.MATCH_PARENT=-1 * LayoutParams.WRAP_CONTENT=-2 * 所以在此處的代碼: * childDimension >= 0 表示子View的寬或高不是MATCH_PARENT和WRAP_CONTENT */ case MeasureSpec.AT_MOST: /** * 當(dāng)父容器的測量模式為AT_MOST時如果: * 子View的寬或高是一個精確的值,比如100px; * 那么: * 子View的size就是childDimension * 子View的mode也為MeasureSpec.EXACTLY */ if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; /** * 當(dāng)父容器的測量模式為AT_MOST時如果: * 子View的寬或高為LayoutParams.MATCH_PARENT * 那么: * 子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size * 子View的mode也為MeasureSpec.AT_MOST */ } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; /** * 當(dāng)父容器的測量模式為AT_MOST時如果: * 子View的寬或高為LayoutParams.WRAP_CONTENT * 那么: * 子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size * 子View的mode也為MeasureSpec.AT_MOST */ } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; /** * 第三種情況: * 父容器的測量模式為UNSPECIFIED * * 請注意兩個系統(tǒng)常量: * LayoutParams.MATCH_PARENT=-1 * LayoutParams.WRAP_CONTENT=-2 * 所以在此處的代碼: * childDimension >= 0 表示子View的寬或高不是MATCH_PARENT和WRAP_CONTENT */ case MeasureSpec.UNSPECIFIED: /** * 當(dāng)父容器的測量模式為UNSPECIFIED時如果: * 子View的寬或高是一個精確的值,比如100px; * 那么: * 子View的size就是childDimension * 子View的mode也為MeasureSpec.EXACTLY */ if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; /** * 當(dāng)父容器的測量模式為UNSPECIFIED時如果: * 子View的寬或高為LayoutParams.MATCH_PARENT * 那么: * 子View的size為0 * 子View的mode也為MeasureSpec.UNSPECIFIED */ } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; /** * 當(dāng)父容器的測量模式為UNSPECIFIED時如果: * 子View的寬或高為LayoutParams.WRAP_CONTENT * 那么: * 子View的size為0 * 子View的mode也為MeasureSpec.UNSPECIFIED */ } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } }
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。