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.
- View Drawing System Knowledge Carding (3) - Detailed Measure of Drawing Process
- View Rendering System Knowledge Carding (4) - Layout Details of Rendering Process
- View Rendering System Knowledge Carding (5) - Draw Details of Rendering Process
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:
- Android Knowledge Catalog: http://www.jianshu.com/p/fd82d18994ce
- Android Interview Document Sharing: http://www.jianshu.com/p/8456fe6b27c4