getWidth()、getMeasuredWidth()区别与用法

85 阅读2分钟

学习目的:

  • 明确 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中获取是拿不到的

image.png

三、思考

  • 什么场景下getWidth()、getMeasuredWidth()两者的值不一致?
  • 在onCreate中获取不到大小,我们要如何去获取大小?