学习目的:
- 明确 getWidth()、getMeasuredWidth() ,这两个API的区别与 使用场景
- 理解在onCreate中获取view大小为何为0
一、背景
日常开发中,我们经常会遇到这一类场景:在页面初始化阶段 动态获取一些View控件的大小,根据这个元素的大小,确定另一个View显示在哪里 这类场景下,我们一般会在Activity的onCreate 中,去获取view控件的大小做处理,此时我们会遇到两个问题,
--获取宽度的API有两个 getWidth()、getMeasuredWidth() ,这两个API的区别是什么?使用场景是什么?
--上述两个API获取的大小都为0,无法获取控件高度,最早可获取界面高度的时机是什么时候?
二、源码解析
2.1明确 getWidth()、getMeasuredWidth() ,这两个API的区别与 使用场景
// 1、返回值 是mMeasuredWidth
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
// 2、全局搜索mMeasuredWidth 赋值 场景,跟踪调用链
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);
}
// 3、由此可见,getMeasuredWidth方法,获取的是View控件在onMeasure 方法中,通过setMeasuredDimension 设置的值
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
总结:getMeasuredWidth 方法返回的实际上是mMeasuredWidth 参数,该参数的赋值是在view的onMeasure方法中进行的,因此在onMeasure 方法执行后能获取到该字段
// 1、获取宽度,返回的实际上是 右侧坐标-左侧坐标的值
public final int getWidth() {
return mRight - mLeft;
}
// 2、跟踪代码,该值在setFrame 中被赋值,跟踪调用链
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
//...
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
//...
return changed;
}
// 3、由此可见,getWidth方法,获取的是View控件在layout方法中,通过setFrame 设置的值
public void layout(int l, int t, int r, int b) {
//...
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
//...
}
结论:
getMeasuredWidth() :在onMeasure过程中赋值的,代表了测量宽度,在onMeasure执行后才能获取到
getWidth() :在layout过程中赋值,代表了实际布局的宽度,在layout执行后才能获取到
应用场景:
getMeasuredWidth():在onLayout()中获取View的宽/高
getWidth():在除onLayout()外的地方获取View的宽/高
2.2 理解在onCreate中获取view大小为何为0
我们在View绘制的过程(onMeasure onLayout onDraw)与Activity的生命周期过程(onCreate onStart onResume)打印了一些日志,我们可以得知,view的绘制是在onResume 后进行的,因此直接在onCreate中获取是拿不到的
三、思考
- 什么场景下getWidth()、getMeasuredWidth()两者的值不一致?
- 在onCreate中获取不到大小,我们要如何去获取大小?