View 的测量宽高和最终宽高有什么区别?
这个问题可以具体为getMeasuredHeight()
和getHeight()
有什么区别。
View
public final int getHeight() {
return mBottom - mTop;
}
再看看 mBottom 、mTop 是怎么来的:
View
protected boolean setFrame(int left, int top, int right, int bottom) {
/**
* 确定子元素的位置
*/
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
}
接着看 setFrame 在哪调用的:
View
public void layout(int l, int t, int r, int b) {
/**
* setFrame设定view的四个顶点的位置,即初始化mLeft、mTop、mBottom、mRight,
* 四个顶点一旦确定,View在父容器的位置也就确定了
*/
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
}
接着看 layout 在哪调用的,首先肯定是父容器确定子元素位置的时候调用的该方法,由于 layout 是view确定自身的位置,onLayout 是确定子元素的位置,因此应该在父容器的 onLayout 中调用的子元素的 layout,拿 LinearLayout 举例来说:
LinearLayout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}
接着看 layoutVertical(l, t, r, b)
,水平方向和竖直方向的类似。
LinearLayout
void layoutVertical(int left, int top, int right, int bottom) {
final int childHeight = child.getMeasuredHeight();
childTop += lp.topMargin;
/**
* 为子元素指定位置
*/
setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);
}
接着看setChildFrame
:
LinearLayout
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
终于在 onlayout 中找到了child.layout
方法,重新屡一下:
则在 View 中getHeight
方法中的mBottom - mTop
是等于 LinearLayout 中setChildFrame
的参数 height,接着查看layoutVertical
方法中的 childHeight 的来源:child.getMeasuredHeight()
,即测量宽高。
默认情况下是上面这种情况,getMeasuredHeight()
和getHeight()
是相等的。但是如果重写 View 的layout()
,那么在 LinearLayout 调用setChildFrame
的时候就会调用重写的 View 的layout()
:
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r+100, b+100);
}
如果按照上面这样重写,那么getHeight()
得到的值会比getMeasuredHeight()
大100。
注意:在某些情况下,View 需要多次 measure 才能确定自己的测量宽高,再前几次的测量过程中,得出的测量宽高有可能和最终宽高不一致,但是到了最后,测量宽高和最终宽高还是相同的,除非有意重写 layout() 修改。
注意
虽说在 Layout 阶段确定 View 的最终宽高,但是在 onLayout 中调用子 view 的getWidth()
经常取不到宽高值,除非子 View 的位置已经确定