============================
原创,转载请指明出处。
在Activity/Fragment的onCreate/onCreateView方法中如果试图获取view的宽和高,如使用getMeasuredWidth
与getMeasuredHeight
方法,会返回0。
众所周知,view显示在屏幕上会经过三个阶段:测量、布局和绘制。其中测量阶段确定整个布局中每个view(包括外层的ViewGroup和内层的子view)的高和宽。布局阶段确定子view在父view的哪个地方(子view的layout_xxx属性就发生作用),布局阶段发生之前,就已经知道了每个view的大小了。绘制阶段则通知子view绘制自己(父view如果不设置背景色的话,就不会发生绘制),绘制阶段之前就已经知道具体的位置了。
所以,在onCreate/onCreateView这两个方法中,在view的measure方法还未执行完时,想获取view的宽和高就会返回0。那么如何在这两个方法中正确的获取宽和高呢?
我常用下面的两种方法:
#1.ViewTreeObserver类
这个类提供了一些回调接口,可以在view的层次结构绘制之前回调、view的层次结构的布局发生变化时回调、view的层次焦点变化时回调等等。具体请看官网资料。
所以在onCreate/onCreateView方法中,可以使用如下代码正确获取view测量后的高和宽:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18final View content = findViewById(android.R.id.content);
//添加布局变化的监听器
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//当这个view的继承层次的布局发生变化时,会回调这个接口。比如测量和布局都完成的时候。
//注意,这里首先要把监听器取消掉,因为如果你在这个接口中又去改变view的布局,会导致死循环。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLEYBEAN) {
//这里要判断一下sdk的版本,下面的方法是在API16及以上才能用
content.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
//下面就可以获取view的宽和高了,因为这已经到了布局阶段了,此时view的测量已经完成了。
int width = content.getMeasuredWidth();
int height = content.getMeasuredHeight();
//下面做其他的操作
}
});
#2.view的post方法
view的post方法实质上使用的是Activity的Handler机制,当post方法中的runnable对象被Handler处理的时候,view层次就已经完成了布局阶段了,此时再获取view的宽和高就可以得到正确的值了。代码如下:1
2
3
4
5
6
7
8
9
10final View content = findViewById(android.R.id.content);
content.post(new Runnable {
@Override
public void run() {
/下面就可以获取view的宽和高了,因为现在已经完成布局阶段了,此时view的测量也已经完成了。
int width = content.getMeasuredWidth();
int height = content.getMeasuredHeight();
//下面做其他的操作
}
});
引用stackoverflow的第二种方法可以看到,google工程师对此方法的解释是:UI事件队列会按事件进入的顺序处理各个事件(Handler、MessageQueue和Loop),在setContentView方法执行之后,事件队列中就会被插入一个要求Activity/Fragment重新布局的消息,所以,当view的post发出的事件执行的时候,整个布局已经完成了,所以此时获取view的高宽是正确的值。
在上面的回答中还有其他几种方法,可以自己去看。