通常情况下我们在onCreate()、onStart()、onResume()获取View的宽高都是0,那是因为View的measure过程和Activity的生命周期不是同步进行的,不能保证在Activity的onCreate()、onStart()、onResume()方法执行时View已经测量完毕,所以不能获取到正确的宽高。下面介绍以下几种获取View宽高的方法:
- Activity/View的onWindowFocusChanged方法体内
- View.post(runnable)方法/Handler.post(new Runnable)
- ViewTreeObserver
- View.measure
- 自定义View时获取宽高
- 通过LayoutParams获取
1、Activity/View的onWindowFocusChanged方法体内
可以在Activity或者View中重写该方法获取,该方法意思是View已经初始化完毕了,既然初始化完毕了自然就可以获取View的宽高了,但是该方法可能执行多次,当焦点变化时都会被调用。
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if(hasWindowFocus){
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
}
2、View.post(runnable)方法/Handler.post(new Runnable)
该方式获取主要是通过view的post将一个Runnable添加到消息队列的尾部,等待Looper调用此Runnable的时候,view也已经初始化好了。同理通过Handler的post方式也是一样,不过Handler必须是主线程的Handler,可以在主线程创建也可以通过new Handler(Looper.getMainLooper)方式创建。
两者的相同之处都是往消息队列尾部添加消息等待执行,不同点是
View要执行必须要保证它已经attached到了Window上。
3、ViewTreeObserver
这个类是整个View树结构的观察者,可以通过该类的OnGlobalLayoutListener接口来获取。 因为当View树的内部结构的View可见性或者状体改变时该接口都会被调用。具体使用如下:
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
注意:removeGlobalOnLayoutListener()方法已经过时,可以调用removeOnGlobalLayoutListener()方法,但是该方法要求API >= 16。
4、View.measure
完整方法名为measure(int widthMeasureSpec,int heightMesureSpec) 通过手动对view测量得到宽高,这种方法较为复杂,要分情况处理,根据view的LayoutParams来分:
- match_parent
根据View的measure过程分析构造此种MeasureSpec需要知道父容器的剩余空间,而此时无法知道,所以理论上不能测出View的大小。 - wrap_content
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec,heightMeasureSpec);
int width = view.getMeasuredWidth());
int height = view.getMeasuredHeight());
注意到(1 << 30)-1,通过分析MeasureSpec的实现可以知道,View的尺寸使用30位二进制表示,也就是说最大是30个1(即2^30 -1),也就是(1 << 30)-1,在最大化模式下我们用View理论上支持的最大值去构造MeasureSpec是合理的。
5、自定义View时获取宽高
有时在自定义控件的时候会需要用到宽高值,大部分的时候可以在onLayout方法里面获取到真实宽高值,同样也可以用上述几种方法获取。首先让自定义的View实现onGlobalListener接口,需要实现onGlobalLayout方法.
然后在onAttachedToWindow()添加
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
记得要在onDetachedFromWindow()删除
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(this);(API 16)
}
接着在onGlobalLayout方法中获取宽高即可,因为要调用多次,可以设立个标志位.
private boolean isFirst = true;
@Override
public void onGlobalLayout() {
if (isFirst) {
int height = getHeight();
isFirst = false;
}
}
6、LayoutParams获取
对于xml文件里面设置了具体宽高的view可以通过view.getLayoutParams().height/width获取到宽高。
优点:能及时获取到,操作简单;
缺点:不够通用,没有设置具体宽高的获取到的值就是0了。