Android中getWidth()和getMeasureWidth()的区别探究

来源:互联网 发布:mac 谷歌浏览器 utf 8 编辑:程序博客网 时间:2024/05/16 09:53

背景

在Android中正确获得View控件的宽和高——使用篇中我们知道了,getWidth和getMeasureWidth都可以获得view的宽,高同理。
那这两个函数究竟有什么区别呢?其实以前我只是知道获取宽高要那样子,也不知道这两个并不知道这两个函数的区别,所以探究了一下。先看个例子。

例子

我直接贴代码:
使用自定义的View:

public class MyView extends View {    private static final String TAG = MyView.class.getSimpleName();    public MyView(Context context) {        super(context);    }    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        Log.d(TAG, "======>"+"onMeasure") ;        printWH();    }    private void printWH() {        Log.d(TAG,"getMeasuredWidth:"+getMeasuredWidth());        Log.d(TAG,"getMeasuredHeight:"+getMeasuredHeight());        Log.d(TAG,"getWidth:"+getWidth());        Log.d(TAG,"getHeight:"+getHeight());    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        Log.d(TAG,"======>"+"onLayout") ;        printWH();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Log.d(TAG,"======>"+"onDraw") ;        printWH();    }}

再Activity中使用自定义的View:

public class MainActivity extends AppCompatActivity {    private static final String TAG = MainActivity.class.getSimpleName();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(new MyView(this));        Log.d(TAG, "onCreate") ;    }}

见证奇迹的时刻:
这里写图片描述
看到了不一样的地方了吧。

原因

源码对比

public final int getWidth() {    return mRight - mLeft;}
public static final int MEASURED_SIZE_MASK = 0x00ffffff;public final int getMeasuredWidth() {   return mMeasuredWidth & MEASURED_SIZE_MASK;}

这两个的源码都很简单,大家应该都能看懂,第二个其实就是因为mMeasuredWidth中前面的8位是状态位,后边的是大小,MEASURED_SIZE_MASK可以过滤出大小。

mMeasuredWidth源码追踪

我们查找mMeasuredWidth赋值的地方。

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {    mMeasuredWidth = measuredWidth;    mMeasuredHeight = measuredHeight;    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;}

然后我们找到使用setMeasuredDimensionRaw的地方:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //两个参数是由parent提供的    boolean optical = isLayoutModeOptical(this);    if (optical != isLayoutModeOptical(mParent)) {        Insets insets = getOpticalInsets();        int oWidth  = insets.left + insets.right;        int oHeight = insets.top  + insets.bottom;        widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);        heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);    }    //将两个参数合并到一个key当中    long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;    if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);    if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||            widthMeasureSpec != mOldWidthMeasureSpec ||            heightMeasureSpec != mOldHeightMeasureSpec) {        // first clears the measured dimension flag        mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;        resolveRtlPropertiesIfNeeded();        int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :                mMeasureCache.indexOfKey(key);        if (cacheIndex < 0 || sIgnoreMeasureCache) {            // 测绘自己            onMeasure(widthMeasureSpec, heightMeasureSpec);            ...        } else {            long value = mMeasureCache.valueAt(cacheIndex);            // 这里使用了我们上面找到的函数,一直追踪可以看到和传入参数的转化过程,我就不详细分析了。我们感兴趣的事这个measure函数            setMeasuredDimensionRaw((int) (value >> 32), (int) value);            ...        }        ...        }        ...    }    ...}

其实差别的根本就和measure函数有很大的关系。我们看一下这个函数的介绍:
This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters.
这个函数就是用来找出这个view究竟应该是多大。他的参数信息是由他的parent提供的。

mRight源码追踪

看完上面的,我们再来追踪一下getWidth()函数中参数的代码。因为有了上面的经验,我们想我们大概也能猜个八九不离十。View中应该也有一个layout()函数。
我们查找一下调出源码:

public void layout(int l, int t, int r, int b) {    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;    }    //前面的我们先不管,这里先存起之前的数据,用来比较是否有改变    int oldL = mLeft;    int oldT = mTop;    int oldB = mBottom;    int oldR = mRight;    // 这里有两个函数,setOpticalFrame(l, t, r, b),setFrame(l, t, r, b)我们去查看一下    boolean changed = isLayoutModeOptical(mParent) ?            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {        //在此处调用的onLayout;        onLayout(changed, l, t, r, b);        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnLayoutChangeListeners != null) {            ArrayList<OnLayoutChangeListener> listenersCopy =                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();            int numListeners = listenersCopy.size();            for (int i = 0; i < numListeners; ++i) {                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);            }        }    }    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;}

setOpticalFrame(l, t, r, b)最终也是调用的setFrame(l, t, r, b),我们就直接看这个函数就行了。

protected boolean setFrame(int left, int top, int right, int bottom) {    boolean changed = false;    ...    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {        changed = true;        // Remember our drawn bit        int drawn = mPrivateFlags & PFLAG_DRAWN;        int oldWidth = mRight - mLeft;        int oldHeight = mBottom - mTop;        int newWidth = right - left;        int newHeight = bottom - top;        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);        // Invalidate our old position        invalidate(sizeChanged);        // 这里给我们的mLeft,mTop,mRight,mBottom,        mLeft = left;        mTop = top;        mRight = right;        mBottom = bottom;        mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);        ...    }    return changed;}

这下知道区别了吧。而view的绘制过程是measure->layout->draw的过程。
所以我们在三个回调中输出,才看出了区别产生了不同的结果。

0 1
原创粉丝点击