方式一:
结果不符合预期。
运行结果:
方式二:
结果符合预期。
如下图:
Android控件宽高的规则:
Android下的控件默认没有宽高,是由父控件给其宽高的。
其中一般的view控件是由(ViewGroup控件:LinearLayout、RelativeLayout 、TableLayout、FrameLayout 、AbsoluteLayou )五大布局给其宽高的。
而像LinearLayout、RelativeLayout等这些最外层的布局控件的宽高是由Android系统的FrameLayout控件设定的。可用android-sdk/tools下的工具hierarchyviewer.bat查看,如下图:
针对方式一的修改方案,如下代码:
运行结果如下:
程序启动时想要获取特定视图组件的尺寸大小,在onCreate中可能无法取到,因为窗口Window对象还没创建完成,还没有执行onMeasure 和 onLayout方法,所以,此时getWidth返回值是0。那么如何或者说何时才能获取控件的大小呢?
方案1:在oncreate方法中, 发一个延迟消息,在handleMesaage中获取控件宽高。原理就是延时一段时间,等待控件测量、布局完成后再获取。(虽然有用但不优雅)
Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case GET_SIZE: int width = listView.getWidth(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_conversation_ui); handler.sendEmptyMessageDelayed(GET_SIZE, 500); }
方案2:获得全局viewTree的观察者,并添加全局的layout监听。原理就是监听onlayout方法执行完成之后,就可以获取控件大小了。
// 获得全局viewTree的观察者,并添加 全局的layout监听 imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver. OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //由于此方法会执行多次,而我们只需要执行一次就可以了, //所以,在执行一次的时候,将全局的layout监听取消,此处this指的是,内部的匿名对象 imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); int width = imageView.getWidth(); } });
方案3:主动通知系统去测量,调用measure(0,0)方法 。再用getMeasureWidth就可以得到测量的结果了。
imageView.measure(0, 0);// 主动通知系统去测量 LogUtils.d("========== imageView getMeasuredWidth: "+imageView.getMeasuredWidth());
方案4:重写Activity的onWindowFocusChanged方法。
在Activity窗口获得或失去焦点时被调用,例如创建时首次呈现在用户面前;当前Activity被其他Activity覆盖;当前Activity转到其他Activity或按Home键回到主屏,自身退居后台;用户退出当前Activity。以上几种情况都会调用onWindowFocusChanged。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
LogUtils.d("========== imageView getWidth: " + imageView.getWidth());
}
从上面代码可以看到,获取控件的宽高有时用getWidth()方法,有时用getMeasuredWidth()方法,这两个方法有什么区别呢?
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返回的是右边坐标减去左边坐标(也就是控件右边距屏幕Y轴的距离减去控件左边距屏幕Y轴的距离),这要在布局之后(也就是onLayout()方法执行完之后)才能确定它们的坐标。在布局后(也就是onLayout()方法执行完之后)才能调用getwidth来获取。
总结:getWidth()获得的宽度是View在设定好布局后整个View的宽度。 这个宽度是指在容器ViewGroup中,该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;
}
注释:return The raw measured width of this view 获得的是原始的测量宽度。onMeasure()方法执行完之后就可以调用该方法获取。
总结:getMeasuredWidth()是对View上的内容进行测量后得到的View内容占据的宽度。这个宽度是根据view中的内容决定。可称为:组件宽度
当然要得到View内容占据的宽度,前提是你必须在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(0,0);(measure中的参数的值你自己可以定义),否则你得到的结果和getWidth()得到的结果是一样的。
比如你定义一个组件的宽50、高50;但是你在布局文件中,硬是要把layout_width指定为match_parent(假设只有一个组件),那这个组件的宽度就被拉到全屏了。这时候getWidth()得到的就是布局宽度。如果要想得到原始宽度,必须调用该组件的measure(0,0)方法重新测量 。再用getMeasureWidth就可以得到了。
两者的使用场合:
getMeasuredWidth:在自定义view重写onLayout时、在我们用layoutinflater动态加载view后想获得view的原始宽度时。
getWidth:一般在view已经布局后呈现出来了,想获取宽度时
layout_grivaty使用时的注意点:
结果如下图:
结果如下图:
区别: