View 繪製體系知識梳理(7) getMeasuredWidth 和 getWidth 的區別

前言

前幾天被問到了getMeasuredWidthgetWidth之間的區別,所以回來看了一下源碼,又順便複習了一遍measure/layout/draw的過程,有興趣的同窗能夠看前面的幾篇文章java

1、getMeasuredWidth 和 getWidth 的定義

1.1 getMeasuredWidth

咱們來看一下getMeasuredWidth方法的內部實現:android

/** * Like {@link #getMeasuredWidthAndState()}, but only returns the * raw width component (that is the result is masked by * {@link #MEASURED_SIZE_MASK}). * * @return The raw measured width of this view. */
    public final int getMeasuredWidth() {
        return mMeasuredWidth & MEASURED_SIZE_MASK;
    }
複製代碼

這裏能夠看到,該方法返回的是setMeasuredDimensionRaw中設置的mMeasuredWidthsize部分,也就是說,該方法返回的是在 測量階段中計算出的寬度面試

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

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

    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;
    }
複製代碼

1.2 getWidth

/** * Return the width of the your view. * * @return The width of your view, in pixels. */
    @ViewDebug.ExportedProperty(category = "layout")
    public final int getWidth() {
        return mRight - mLeft;
    }
複製代碼

getWidth的值是根據mRightmLeft之間的差值計算出來的,在setFrame方法中,會對View的四個點座標mLeft/mRigth/mTop/mBottom進行賦值,它決定了View在其父容器中所處的位置:bash

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;
            
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;

            //....
        }
        return changed;
    }
複製代碼

setFrame就是在layout過程當中調用的:ide

public void layout(int l, int t, int r, int b) {

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        //經過以前介紹的 setFrame 方法進行賦值。
        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);

            if (shouldDrawRoundScrollbar()) {
                if(mRoundScrollbarRenderer == null) {
                    mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
                }
            } else {
                mRoundScrollbarRenderer = null;
            }

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

    }
複製代碼

1.3 小結

在以前的兩篇文章 View 繪製體系知識梳理(3) - 繪製流程之 Measure 詳解View 繪製體系知識梳理(4) - 繪製過程之 Layout 詳解,咱們介紹了measurelayout的內部實現,而getMeasuredWidthgetWidth就分別對應於上面這兩個階段得到的寬度,也就是說:oop

  • getMeasuredWidthmeasure階段得到的View的原始寬度。
  • getWidthlayout階段完成後,其在父容器中所佔的最終寬度

1.4 註釋說明

下面是Google文檔中對於上面這兩個方法的註釋說明:佈局

  • getMeasuredWidth
The width of this view as measured in the most recent call to measure(). 
This should be used during measurement and layout calculations only. 
Use getWidth() to see how wide a view is after layout.

Returns: the measured width of this view
複製代碼
  • getWidth
Return the width of the your view.

Returns: the width of your view, in pixels
複製代碼

2、示例

咱們用一個簡單的示例,來演示getMeasuredWidthgetWidth的區別:this

2.1 佈局定義

首先定義一個自定義的LinearLayout,它包含兩個子View,在xml中它們的寬度都被指定爲200dpspa

<?xml version="1.0" encoding="utf-8"?>
<com.demo.lizejun.repoopt.WidthDemoLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:background="@android:color/holo_blue_bright"
        android:layout_width="200dp"
        android:layout_height="100dp"/>
    <View
        android:background="@android:color/holo_orange_light"
        android:layout_width="200dp"
        android:layout_height="100dp"/>
</com.demo.lizejun.repoopt.WidthDemoLayout>
複製代碼

2.2 重寫 onLayout 方法

WidthDemoLayout中,咱們重寫它的onLayout方法,對它的子View從新擺放,並打印出getMeasuredWidthgetWidth方法的值:code

public class WidthDemoLayout extends LinearLayout {

    public WidthDemoLayout(Context context) {
        super(context);
    }

    public WidthDemoLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WidthDemoLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (i == childCount - 1) {
                child.layout(child.getLeft() ,child.getTop(), child.getRight() + 400, child.getBottom());
            }
            Log.d("WidthDemoLayout", "measuredWidth=" + child.getMeasuredWidth() + ",width=" + child.getWidth());
        }
    }
}
複製代碼

輸出的結果爲:

>> 12-04 12:48:58.788 24935-24935/com.demo.lizejun.repoopt D/WidthDemoLayout: measuredWidth=800,width=800
>> 12-04 12:48:58.788 24935-24935/com.demo.lizejun.repoopt D/WidthDemoLayout: measuredWidth=800,width=1200
複製代碼

最終展現的時候,雖然咱們在xml中指定了相同的寬度,可是最終顯示是以getWidth爲準:


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索