android中getWidth()和getMeasuredWidth()之間的區別


     先給出一個結論:getMeasuredWidth()獲取的是view原始的大小,也就是這個view在XML文件中配置或者是代碼中設置的大小。getWidth()獲取的是這個view最終顯示的大小,這個大小有可能等於原始的大小也有可能不等於原始大小。

     從源碼上開始分析一下這兩個方法的區別。首先來看一下getMeasuredWidth()這個方法。

 1   public final int getMeasuredWidth() {
 2         return mMeasuredWidth & MEASURED_SIZE_MASK;
 3     }
 4 
 5   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 6  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
 7                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 8     }
 9 
10   protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
11         boolean optical = isLayoutModeOptical(this);
12         if (optical != isLayoutModeOptical(mParent)) {
13             Insets insets = getOpticalInsets();
14             int opticalWidth  = insets.left + insets.right;
15             int opticalHeight = insets.top  + insets.bottom;
16 
17             measuredWidth  += optical ? opticalWidth  : -opticalWidth;
18             measuredHeight += optical ? opticalHeight : -opticalHeight;
19         }
20  setMeasuredDimensionRaw(measuredWidth, measuredHeight);
21     }
22 
23   private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
24         mMeasuredWidth = measuredWidth;
25         mMeasuredHeight = measuredHeight;
26 
27         mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
28     }

   從源碼上來看,getMeasuredWidth()獲取的是mMeasuredWidth的這個值。這個值是一個8位的十六進制的數字,高兩位表示的是這個measure階段的Mode的值,具體可以查看MeasureSpec的原理。這里mMeasuredWidth & MEASURED_SIZE_MASK表示的是測量階段結束之后,view真實的值。而且這個值會在調用了setMeasuredDimensionRaw()函數之后會被設置。所以getMeasuredWidth()的值是measure階段結束之后得到的view的原始的值。

 

     再來看看getWidth()的源碼:

1   public final int getWidth() {
2         return mRight - mLeft;
3     }

   那么問題來了,mRight和mLeft是什么值,是在什么時候被設置的。我們再看layout階段的源碼:

 1   public void layout(int l, int t, int r, int b) {
 2         if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
 3             onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
 4             mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
 5         }
 6 
 7         int oldL = mLeft;
 8         int oldT = mTop;
 9         int oldB = mBottom;
10         int oldR = mRight;
11 
12         boolean changed = isLayoutModeOptical(mParent) ?
13  setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
14 
15       ... 
16 }

   在layout階段會去調用setOpticalFrame()或者調用setFrame()方法,從源碼中可知setOpticalFrame()方法,最終還是調用的setFrame()方法。

 1   protected boolean setFrame(int left, int top, int right, int bottom) {
 2         boolean changed = false;
 3 
 4         if (DBG) {
 5             Log.d("View", this + " View.setFrame(" + left + "," + top + ","
 6                     + right + "," + bottom + ")");
 7         }
 8 
 9         if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
10             changed = true;
11 
12             // Remember our drawn bit
13             int drawn = mPrivateFlags & PFLAG_DRAWN;
14 
15             int oldWidth = mRight - mLeft;
16             int oldHeight = mBottom - mTop;
17             int newWidth = right - left;
18             int newHeight = bottom - top;
19             boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
20 
21             // Invalidate our old position
22             invalidate(sizeChanged);
23 
24             mLeft = left;
25             mTop = top;
26             mRight = right;
27             mBottom = bottom;
28             mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
29 
30             mPrivateFlags |= PFLAG_HAS_BOUNDS;
31    ...
32 }

  所以最終的mLeft和mRight的值是在setFrame()方法中被設置的。而且這些mLeft,mRight代表了view最終顯示在界面中的大小。

 

    下面我們自定義一個簡單的ViewGroup,在layout階段改變left,right的值,觀察getMeasuredWidth()和getWidth()方法之間的區別。

 1 package com.gearmotion.app.customviewgroup;
 2 
 3 import android.content.Context;
 4 import android.util.AttributeSet;
 5 import android.view.View;
 6 import android.widget.RelativeLayout;
 7 
 8 /**
 9  * Created by Charles on 2015/11/21.
10  */
11 public class CustomViewGroup extends RelativeLayout {
12 
13     public CustomViewGroup(Context context) {
14         super(context);
15     }
16 
17     public CustomViewGroup(Context context, AttributeSet attrs) {
18         super(context, attrs);
19     }
20 
21     public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
22         super(context, attrs, defStyleAttr);
23     }
24 
25     @Override
26     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
27         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
28     }
29 
30     @Override
31     protected void onLayout(boolean changed, int l, int t, int r, int b) {
32         super.onLayout(changed, l, t, r, b);
33         View child = this.getChildAt(1);  //the textview
34         //add 100px for right
35         child.layout(child.getLeft(), child.getTop(),child.getRight() + 100,child.getBottom());
36 
37 
38     }
39
<?xml version="1.0" encoding="utf-8"?>
<com.gearmotion.app.customviewgroup.CustomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gearmotion.app.customviewgroup.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/left"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="width" />

        <Button
            android:id="@+id/right"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="measuredWidth" />
    </LinearLayout>

    <TextView
        android:id="@+id/textview"
        android:layout_width="100px"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="#8EE5EE"
        android:gravity="center"
        android:text="textview" />

</com.gearmotion.app.customviewgroup.CustomViewGroup>
package com.gearmotion.app.customviewgroup;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    TextView mTextView;
    Button mLeftBtn;
    Button mRightBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) this.findViewById(R.id.textview);
        mLeftBtn = (Button) this.findViewById(R.id.left);
        mRightBtn = (Button) this.findViewById(R.id.right);
        mLeftBtn.setOnClickListener(this);
        mRightBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.left:   //width
                Toast.makeText(MainActivity.this, "width:" + mTextView.getWidth(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.right:  //measuredWidth
                Toast.makeText(MainActivity.this,"measuredWidth:"+mTextView.getMeasuredWidth(),Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

 在這個demo中,我們給textview設置寬度為100px,但是在layout階段給它加大到200,最終結果是:點擊width按鈕,顯示為200,點解measuredWidth按鈕顯示為100.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM