我们平常见的最多的这个ViewGroup,都是通过Linlayout和Relayout来通过继承ViewGroup的方式
ViewGroup绘制布局呢,它会遍历所有的子View
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);
}
}
}
我们点击measureChild方法看看
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);
}
这里的这部分代码我们之前讲解 Measure 基础学习的时候,我就有讲过。
我们ViewGroup是一个抽象类,然后它具体怎么去测量的过程并没有写在ViewGroup里面,它是写在我们Linlayout、Relayout里面的。我们今天就只看Linlayout的一个具体的测量过程
所以我们直接去Linalyout的onMeasure方法查看
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {//竖直的方向
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
如果方向是Vertical就采用第一种,否则就是第二种
我们直接来看第一个的好了。
measureVertical:
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
....这里省略非常多的代码
for (int i = 0; i < count; ++i) { //子布局的大小开始测量
....省略代码
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
skippedMeasure = true;
} else {
if (useExcessSpace) {
lp.height = LayoutParams.WRAP_CONTENT;
}
final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
这里我们需要注意,按我们翻译过来的意思就是,测量之前需要布局,这部分我们下一篇会提到
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
final int childHeight = child.getMeasuredHeight();
....省略代码了
}//for循环结束
//这里开始测量Llinlayout自己的大小了
//padding内边距的相加 ,
mTotalLength += mPaddingTop + mPaddingBottom;
int heightSize = mTotalLength;
//=================
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
//==========这里就有点类似View里面的测量方式了,都是去取最大的值
//getSuggestedMinimumHeight()这个方法跟View里面的getSuggestedMinimumHeight()一样的,
看是否有背景大小,然后再看最低的高度是多少(android:MinHeight),
让背景大小跟minHeight相互比较,取最大的那个。
//resolveSizeAndState里面呢,根据测量模式的不同,
来判断是直接获取padding的大小还是直接获取父容器给的指定空间大小,
不管它取哪个值,它最后的大小不能超过父容器给的空间的
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
......省略代码
//这里就设置了最后的布局大小了
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
resolveSizeAndState方法
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
//这里的size是paddingTop/paddingBottom的总和
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
//根据测量的模式,来判断取哪个值
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
从上面我们可以知道我们最后的结果,受到父容器的measureSpec影响,和padding影响。
有时候系统可能会多次调用measure才可以正确的测量到宽高,如果我们在onMeasure过程中去获取测量的宽高,是不能够准确的,所以我们可以采用如下的方式来获取,要等他measure完毕
public void onStart(){
super.onStart();
view.post(new Runnable()){
public void run(){
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
});
}
//我们发送一个消息到队里的尾部,当我们Looper调用此runnalber的时候,
表示view已经初始化好了一切了
学习资源书籍:《Android 开发艺术探索》
网友评论