关于View的getWidth()和getMeasuredWidth()的知识总结

1、View的getWidth()和getMeasuredWidth()有什么区别吗?

View 的宽高是由 View 本身和 parent 容器共同决定的。

getMeasuredWidth() 与 getWidth() 分别对应于视图绘制 的 measure 与 layout 阶段。
getMeasuredWidth()(int widthMeasureSpec, int heightMeasureSpec)方法里面获得的,而getWidth()是在onLayout()方法中获得的。大部分情况下他们是相等,也有一些不相等的情况。比如在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(0,0);(measure中的参数的值你自己可以定义)的话,两者的结果可能会不同。

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()返回的是右边坐标减轻坐标减去左边坐标,这要在布局之后才能确定它们的坐标,也就是说在布局后onLayout()方法里面才能调用getWidth()来获取。所以getWidth()获得的宽度是View在设定好布局后整个View的宽度。
getMeasuredWidth()源码:
/**
     * 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;
    }
从源码可以看出getMeasuredWidth()返回的是此视图的原始测量宽度。所以说getMeasuredWidth()是对View上的内容进行测量后得到的View内容占据的宽度。

一般情况下,getMeasuredWidth()和getWidth()获得的结果是一样的,但是如果在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(0,0);(measure中的参数的值你自己可以定义)的话,两者的结果可能会不同。

例如:

public class CustomView extends android.support.v7.widget.AppCompatTextView {
    
    private String TAG = CustomView.class.getSimpleName();
    
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public CustomView(Context context, 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 left, int top, int right, int bottom) {
        super.onLayout(changed, left + 10, top, right + 10, bottom + 10);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        measure(0, 0);
        Log.d(TAG, "width=" + getWidth());
        Log.d(TAG, "height=" + getHeight());
        Log.d(TAG, "MeasuredWidth=" + getMeasuredWidth());
        Log.d(TAG, "MeasuredHeight=" + getMeasuredHeight());
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context="com.shzx.viewdemo.MainActivity">
    <com.shzx.viewdemo.CustomView
        android:id="@+id/customView"
        android:layout_width="100dp"
        android:text="ceshi"
        android:layout_height="100dp" />
</LinearLayout>

此时的打印结果是

12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: width=350
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: height=350
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: MeasuredWidth=116
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: MeasuredHeight=66

此时getWidth()和getMeasuredWidth()不相等

因此可以得出 getWidth(): View在设定好布局后整个View的宽度。getMeasuredWidth(): 对View上的内容进行测量后得到的View内容佔据的宽度

2、如何在onCreate中拿到View的宽度和高度?

在onCreate()中获取View的宽高有三种方法:
获取view的宽高

private void getWidthHeight() {
        int width = customView.getWidth();
        int height = customView.getHeight();
        LogUtils.e("width =" + width);
        LogUtils.e("height =" + height);

        int measuredWidth = customView.getMeasuredWidth();
        int measuredHeight = customView.getMeasuredHeight();
        LogUtils.e("measuredWidth =" + measuredWidth);
        LogUtils.e("measuredHeight =" + measuredHeight);
    }

1)、View.post(runnable)

利用 Handler 通信机制,发送一个 Runnable 到 message queue 中,当 view layout 处理完成时,自动发送消息,通知 UI 线程。借此机制,巧妙获取 View 的宽高属性。代码简洁,使用简单,相比 ViewTreeObserver 监听处理,还不需要手动移除观察者监听事件。

 customView.post(new Runnable() {
            @Override
            public void run() {
            getWidthHeight();
            }
        });

打印结果:

12-16 02:33:49.938 4238-4238/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=23 ] 
12-16 02:33:49.941 4238-4238/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=24 ] 
12-16 02:33:49.941 4238-4238/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=28 ] 
12-16 02:33:49.946 4238-4238/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=29 ] 

2)、ViewTreeObserver.addOnGlobalLayoutListener(OnGlobalLayoutListener listener)

监听 View 的 onLayout() 绘制过程,一旦 layout 触发变化,立即回调 onLayoutChange 方法。

注意,使用完也要注意调用 removeOnGlobalLayoutListener() 方法移除监听事件。避免后续每一次发生全局 View 变化均触发该事件,影响性能。

ViewTreeObserver observer = customView.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getWidthHeight();
                customView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

打印结果:

12-16 03:34:06.981 6116-6116/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=61 ] 
12-16 03:34:06.982 6116-6116/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=62 ] 
12-16 03:34:06.983 6116-6116/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=66 ] 
12-16 03:34:06.983 6116-6116/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=67 ] 

3)、View.measure(int widthMeasureSpec,int heightMeasureSpec)

View.MeasureSpec.makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode)

其中 MeasureSpec.MODE_SHIFT = 30

size最大值为 (1<<30)-1, 即用30位二进制表示,也就是说最大是30个1 (即2^30-1)。

        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY);
        int heigthMeasureSpec = View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY);

        customView.measure(widthMeasureSpec,heigthMeasureSpec);

        int width = customView.getWidth();
        int height = customView.getHeight();
        LogUtils.e("width =" + width);
        LogUtils.e("height =" + height);

        int measuredWidth = customView.getMeasuredWidth();
        int measuredHeight = customView.getMeasuredHeight();
        LogUtils.e("measuredWidth =" + measuredWidth);
        LogUtils.e("measuredHeight =" + measuredHeight);

打印结果:

12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: width =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=42 ] 
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: height =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=43 ] 
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: measuredWidth =200>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=47 ] 
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: measuredHeight =200>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=48 ] 
        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
        int heigthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);

        customView.measure(widthMeasureSpec,heigthMeasureSpec);

        int width = customView.getWidth();
        int height = customView.getHeight();
        LogUtils.e("width =" + width);
        LogUtils.e("height =" + height);

        int measuredWidth = customView.getMeasuredWidth();
        int measuredHeight = customView.getMeasuredHeight();
        LogUtils.e("measuredWidth =" + measuredWidth);
        LogUtils.e("measuredHeight =" + measuredHeight);

打印结果:

12-16 03:03:28.394 5344-5344/com.shzx.viewdemo E/MainActivity: width =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=42 ] 
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: height =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=43 ] 
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=47 ] 
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=48 ] 

除了在onCreate()中获得View的宽高,还可以在Activity的onWindowFocusChanged(boolean hasFocus)方法中获取宽高

源码:

 /**
     * Called when the current {@link Window} of the activity gains or loses
     * focus.  This is the best indicator of whether this activity is visible
     * to the user.  The default implementation clears the key tracking
     * state, so should always be called.
     *
     * <p>Note that this provides information about global focus state, which
     * is managed independently of activity lifecycles.  As such, while focus
     * changes will generally have some relation to lifecycle changes (an
     * activity that is stopped will not generally get window focus), you
     * should not rely on any particular order between the callbacks here and
     * those in the other lifecycle methods such as {@link #onResume}.
     *
     * <p>As a general rule, however, a resumed activity will have window
     * focus...  unless it has displayed other dialogs or popups that take
     * input focus, in which case the activity itself will not have focus
     * when the other windows have it.  Likewise, the system may display
     * system-level windows (such as the status bar notification panel or
     * a system alert) which will temporarily take window input focus without
     * pausing the foreground activity.
     *
     * @param hasFocus Whether the window of this activity has focus.
     *
     * @see #hasWindowFocus()
     * @see #onResume
     * @see View#onWindowFocusChanged(boolean)
     */
    public void onWindowFocusChanged(boolean hasFocus) {
    }
 @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        getWidthHeight();
    }

打印结果:

12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=58 ] 
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=59 ] 
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=63 ] 
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=64 ] 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容