View的結(jié)構(gòu)

Android中的View以樹(shù)的結(jié)構(gòu)來(lái)進(jìn)行管理,從如中可以看到,最頂層的Window到ContentView,這些都是我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中比較少用到的。
MeasureSpec
MeasureSpec是一個(gè)int類(lèi)型的變量,以下為MeasureSpec的部分源代碼:
private 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_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
可以看到MeasureSpec用最高的兩位用來(lái)存放測(cè)量的模式:
UNSPECIFIED:大小不確定的
EXACTLY:大小確定了具體的
AT_MOST:大小的上限,即最大不能超過(guò)這個(gè)
ViewGroup的測(cè)量
View一開(kāi)始從DecorView開(kāi)始進(jìn)行measure,由于DecorView本質(zhì)是FrameLayout即就是ViewGroup,所以我們一開(kāi)始從ViewGroup開(kāi)始講onMeasure。
ViewGroup從被measure調(diào)用之后開(kāi)始調(diào)用onMeasure開(kāi)始測(cè)量自己;
ViewGroup從onMeasure函數(shù)中獲得的widthMeasureSpec和heightMeasureSpec,根據(jù)這兩個(gè)變量的值來(lái)進(jìn)行對(duì)子View的測(cè)量。遍歷子View更具子View的LayoutParam進(jìn)行下一步的MeasureSpec計(jì)算,即調(diào)用getChildMeasureSpec(measureSpec,padding,size);將得到的結(jié)果傳入到measure中進(jìn)行子View的onMeasure。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
super.onMeasure(widthSpec, heightSpec);
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
LayoutParams lp = view.getLayoutParams();
int childMeasureWidth = getChildMeasureSpec(widthMeasureSpec,0,lp.width);
int childMeasureHeight = getChildMeasureSpec(heightMeasureSpec,0,lp.height);
view.measure(childMeasureWidth,childMeasureHeight);
//measureChild(view, widthSpec, heightSpec);
}
}
這里假設(shè)ViewGroup的大小是確定的且為寬高都為500個(gè)像素,為什么是500個(gè)像素,后面再講。接著往下走,遍歷每一個(gè)子View,根據(jù)子View的布局參數(shù)來(lái)計(jì)算以下的子View的測(cè)量規(guī)格即調(diào)用了getChildMeasureSpec,接著將處理過(guò)的測(cè)量規(guī)格傳給子View進(jìn)行measure,接著跟蹤進(jìn)measure,measure中代碼很長(zhǎng)包含了measure緩存的處理比較復(fù)雜,這里就不貼出來(lái)了,view的measure代碼大致上就是調(diào)用了View的onMeasure,接著看看View的onMeasure是怎么實(shí)現(xiàn)的:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
可以看到直接通過(guò)setMeasuredDimension把size提交上去了,這里的getDefaultSize和getSuggestedMinimumWidth依次:
根據(jù)傳入的測(cè)量規(guī)格和建議的默認(rèn)大小進(jìn)行實(shí)際的大小確定:
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 MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
默認(rèn)建議的大小是根據(jù)View的背景大小來(lái)確定的,如果背景Drawable是空的話那就是默認(rèn)的mMinHeight的值,該變量默認(rèn)值是0,否則的話就取背景的最小尺寸
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
通過(guò)以上步驟,View的測(cè)量就完成了,當(dāng)然,對(duì)于子View中的ViewGroup的測(cè)量,遞歸上述過(guò)程就OK了。
補(bǔ)充
measureChild
在View的measure中:
int childMeasureWidth = getChildMeasureSpec(widthMeasureSpec,0,lp.width);
int childMeasureHeight = getChildMeasureSpec(heightMeasureSpec,0,lp.height);
view.measure(childMeasureWidth,childMeasureHeight);
這段代碼可以替換成:
measureChild(view, widthSpec, heightSpec);
他倆的實(shí)際作用是差不多的,查看系統(tǒng)源碼可以一目了然的看到:
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
measureChildren
measureChildren實(shí)際上是遍歷各個(gè)子View,然后分別調(diào)用measureChild:
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}