Android MeasureSpec的理解和源碼的解析
更新時間:2017年09月05日 09:26:54 投稿:lqh
這篇文章主要介紹了Android MeasureSpec的理解和源碼的解析的相關(guān)資料,希望通過本文大家可以掌握理解這部分內(nèi)容,需要的朋友可以參考下
Android MeasureSpec的理解和源碼的解析
MeasureSpec的創(chuàng)建規(guī)則:

實例詳解:
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或者MeasureSpec.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ù)點的討論,請看下面的源碼分析.
*
*/
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.
* 在此分為三種情況進行.
*/
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);
}
}
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
Android側(cè)滑菜單控件DrawerLayout使用詳解
這篇文章主要為大家詳細(xì)介紹了Android側(cè)滑菜單控件DrawerLayout的使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
Android判斷網(wǎng)絡(luò)狀態(tài)的代碼
這篇文章主要為大家詳細(xì)介紹了Android判斷網(wǎng)絡(luò)狀態(tài)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
Android14原生PackageInstaller安裝某些apk報錯問題
本文主要介紹了在Android 14上安裝大型應(yīng)用時遇到的java.lang.RuntimeException: Could not copy bitmap to parcel blob錯誤,下面就一起來介紹一下解決方法,感興趣的可以了解一下2025-03-03

