我们经常会需要获取View的宽度或者高度,但是我们会发现不管是在onCreate还是onStart,onResume里面获取到的值都是0.
这是因为View的measure过程和Activity的生命周期方法不是同步进行的,不能保证在Activity的onCreate,onStart,onResume方法执行时View已经测量完毕,所以不能获得正确宽高值。
因此我们需要通过其他方法来实现,如下几种方法都可以获取正确宽高值
这个方法的含义是View已经初始化完毕了,自然可以获取宽高,但是该方法有可能多次执行,当Activity焦点变化的时候都会被调用。
public void onWindowFocusChanged(boolean hasFocus){
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
}
通过view的post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View也已经初始化好了。
protected void onStart(){
super.onStart();
view.post(new Runnable);
@Override
public void run() {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
}
这里在onCreate 和 onResume里面都可以。
顾名思义这个类是整个View树结构的观察者,通过它的众多回调可以获取到view的宽高,比如onGlobalLayoutListener接口,当View树的状体改变或者View树内部的View的可见性发生改变时该接口都将被回调,显然onGlobal也会被多次调用.
protected void onResume() {
super.onResume();
ViewTreeObserver observer = btn.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
btn.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = btn.getMeasuredWidth();
int height = btn.getMeasuredHeight();
}
});
}
这里的removeOnGlobalLayoutListener方法需要API 16。
该方法有两个参数即宽高的MeasureSpec,完整方法名为measure(int widthMeasureSpec,int heightMesureSpec) 如果对此不了解的可以先去学习关于view的measure过程.
通过手动对view测量得到宽高,这种方法较为复杂,要分情况处理,根据view的LayoutParams来分
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是合理的。
另外关于View的measure,网络上有两个错误的用法:
1.
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec,heightMeasureSpec);
2.
view.measure(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
有时我们在自定义控件的时候会需要用到宽高值,大部分的时候可以在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;
}
}
(此文摘录于书籍《Android开发艺术探索》 任玉刚 著
刚哥博客 http://blog.csdn.net/singwhatiwanna/article/details/49230997)