天天看點

分析View的onMeasure的底層實作

啥都不說,先上源代碼

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 //這在官方的文檔中有說明,讓我們當重寫這個方法時,必須調用,不然會報非法異常
}
           

底層調用了setMeasuredDimention傳入預設的大小,參數為getSuggestedMinimunWidth()的傳回值,那麼getSuggestedMinimunWidth()做了什麼,我們看看

protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
//這裡的mBackground是Drawable對象
}
           

傳回的背景圖檔的最小值與視圖的最小值的最大值。

何以證明?

那麼我們就檢視一下mBackGround的由來。我檢視了setBackgroundColor

setBackgroundResource的源代碼,發現最終都是調用了setBackground。而setBackground又在底層調用了setBackgroundDrawable()方法

我就在這裡找了一下,果不其然,被我發現了

if (mBackground == null
        || mBackground.getMinimumHeight() != background.getMinimumHeight()
        || mBackground.getMinimumWidth() != background.getMinimumWidth()) {
    requestLayout = true;
}

// Set mBackground before we set this as the callback and start making other
// background drawable state change calls. In particular, the setVisible call below
// can result in drawables attempting to start animations or otherwise invalidate,
// which requires the view set as the callback (us) to recognize the drawable as
// belonging to it as per verifyDrawable.
mBackground = background;
if (background.isStateful()) {
    background.setState(getDrawableState());
}
if (isAttachedToWindow()) {
    background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
           

上面明顯有一個指派過程,當然這都不是重點。有興趣可以去看看。

那麼傳回來我們看看getDefaultSize()

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED://不限制子視圖的大小
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}
           

我們發現當Measure.mode==UNSPECIFIED,也即是不限制子視圖的大小的時候,getDefault将傳回背景圖檔的最小值與視圖的最小值的最大值。當然在一般情況下,就是MeasureSpec.getSize的大小。

進一步,我們裡來到了setMeasuredDmention()并将解析到的資料傳入。

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);
}
           

我們發現在這裡,這是對值進行了處理,

之後就傳給了setMeasuredDimentionRaw(),那麼在setMeasuredDimentionRaw又做了什麼?,我們瞧瞧看。

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;

    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
           

将值傳遞給了View的測量寬度。那麼我們就要想,之前使用getMeasureWidth()和getMeasureHeight()是不是傳遞的就是這個值呢?

我們看看去.

public final int getMeasuredWidth() {
    return mMeasuredWidth & MEASURED_SIZE_MASK;
}
           

這是寬度的傳回值。到這裡你可能認為我真的去算!!!!!!我隻能說你想多了

虧自己還是寫程式的,自己測一下就是喽

public void getAnd(){   
        int a=;
        System.out.println(a&);
    }
           

帶了個值試了一下,結果還是原來的資料看來自己猜的是對的。

也就是說我們通過setMesureDimention(int,int)來設定的值就是我們通過getMeasureWidth()和getMeasureHeight(int,int)設定的值。
           

水準有限,如有錯誤,請指出