View Drawing System Knowledge Carding (7) - The Difference between getMeasuredWidth and getWidth

Keywords: Android xml Google encoding

Preface

A few days ago, I was asked about the difference between getMeasuredWidth and getWidth, so I went back to the source code and reviewed the process of measure/layout/draw. Interested students can read the previous articles.

Definitions of getMeasuredWidth and getWidth

1.1 getMeasuredWidth

Let's look at the internal implementation of the getMeasuredWidth method:

    /**
     * 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;
    }

Here you can see that this method returns the size part of mMeasuredWidth set in setMeasuredDimensionRaw, that is to say, the width calculated in the measurement phase is returned by this method.

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

The value of getWidth is calculated based on the difference between mRight and mLeft. In the setFrame method, the four coordinates of mLeft/mRigth/mTop/mBottom of View are assigned, which determines the position of View in its parent container:

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

The setFrame is called in the layout process:

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

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        //Assignment is done through the setFrame method introduced earlier.
        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 summary

Two previous articles View Drawing System Knowledge Carding (3) - Detailed Measure of Drawing Process and View Rendering System Knowledge Carding (4) - Layout Details of Rendering Process We introduced the internal implementations of measure and layout, and getMeasuredWidth and getWidth correspond to the widths obtained in the above two stages respectively, that is to say:

  • getMeasuredWidth is the original width of the View obtained during the measure ment phase.
  • getWidth is the final width it occupies in the parent container after the layout phase is completed

1.4 Notes

Following are the annotations to the above two methods in the Google Document:

  • 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

Two, example

Let's use a simple example to demonstrate the difference between getMeasuredWidth and getWidth:

2.1 Layout Definition

First, define a custom Linear Layout, which contains two sub-View s whose widths are specified as 200 DP in xml.

<?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 Rewriting onLayout Method

In WidthDemoLayout, we rewrite its onLayout method, rearrange its subviews, and print out the values of getMeasuredWidth and getWidth methods:

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

The output is as follows:

>> 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

In the final display, although we specify the same width in xml, the final display is based on getWidth:

For more articles, please visit my Android Knowledge Carding Series:

Posted by titaniumtommy on Sun, 19 May 2019 00:03:51 -0700