测量View(一):创建View并测量 //www.greatytc.com/p/4fb206b947ee
测量View(三):获得测量宽高及真实宽高 //www.greatytc.com/p/cbd758a5b5cf
测量的宽高 与真实宽高 的区别
很简单用ScrollView举例,当ScrollView中有很多内容一屏显示不全时,此时测量的高度(HorizontalScrollView宽度)是超过屏幕的高度(宽度)的,但是手机最终展示给用户时的真实宽高所占的尺寸最多不超过屏幕的尺寸。
测量的宽高
在正确获得View的宽高前,我们来分析一下原码
view.getMeasuredWidth();
view.getMeasuredHeight();
View:getMeasuredWidth();
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
View:getMeasuredHeight();
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
以上返回的是该 View 的原始宽度和高度
查看View对mMeasuredWidth及mMeasuredHeight的引用发现setMeasuredDimensionRaw方法对其进行赋值:
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
回看调用顺序
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
以上这里看到了熟悉的方法onMeasure() ;
小结:如果想得到measuredWidth 及measuredHeight 要调用 onMeasure() 即:measure()
真实的宽高
view.getWidth();
view.getHeight();
View:getWidth();
public final int getWidth() {
return mRight - mLeft;
}
View.getHeight();
public final int getHeight() {
return mBottom - mTop;
}
以上涉及到四个变量不难看出为View的坐标
同样的方法找到几个相应的方法
public final void setLeft(int left) {
...
mLeft = left;
...
}
public final void setRight(int right) {
...
mRight = right;
...
}
public final void setTop(int top) {
...
mTop = top;
...
}
public final void setBottom(int bottom) {
...
mBottom = bottom;
...
}
细心的朋友已经发现以上方法为final的
而且查看注释发现:
This method is meant to be called by the layout system and should not generally be called otherwise, because the property may be changed at any time by the layout.
简单说就是该方法应由布局系统调用,不能随便调用
继续找发现:
protected boolean setFrame(int left, int top, int right, int bottom) {
...
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
...
}
/**
* Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
* @hide
*/
//与setFrame相同, 但是@hide被隐藏
public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
setFrame(left, top, right, bottom);
}
public void layout(int l, int t, int r, int b) {
...
boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
...
}
//最终还是调用的setFrame()方法
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
Insets parentInsets = mParent instanceof View ?
((View) mParent).getOpticalInsets() : Insets.NONE;
Insets childInsets = getOpticalInsets();
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
以上又看到我们熟悉的方法
要获得View的真实宽高,需要调用 layout()
总结:
getWidth(): View在设定好布局后的宽。
getMeasuredWidth(): 对View测量后得到的测量宽度