前言
在Activity获取控件宽高时,有时获取的为0,无法获取正确的信息,原因是View的measure过程和Activity的生命周期不是同步的,也就是说在Activity走了onCreate、onStart、onResume周期后,并不一定View能测绘完成。
解决方式
- 在
onWindowFocusChanged
方法中做监听
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus){
int width = txt.getMeasuredWidth();
int height = txt.getMeasuredHeight();
Log.e("测试","onWindowFocusChanged: "+width+"---"+height);
}
}
此方法,在Activity失去或者获取到焦点时,会多次调用。
- 采用
post
方法
//txt是一个view
txt.post(new Runnable() {
@Override
public void run() {
int width = txt.getMeasuredWidth();
int height = txt.getMeasuredHeight();
Log.e("测试","post: "+width+"---"+height);
}
});
post
将一个Runnable
放到消息队列中,运行在UI线程中,此Runnable所在的队列,会在view被attached时,进行调用,所以此时,view已经初始化好。
ViewTreeObserver
方式
ViewTreeObserver observer = txt.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
txt.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = txt.getMeasuredWidth();
int height = txt.getMeasuredHeight();
Log.e("测试","ViewTreeObserver: "+width+"---"+height);
}
});
当视图树的状态发生改变或者视图的可见性发生改变,onGlobalLayout
将会被回调,此监听会多次回调,所以要及时注销监听。
- view.measure(int widthMeasureSpec, int heightMeasureSpec) 手动测量
private void measuWidthHeight(){
//具体值
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY);
//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);
txt.measure(widthMeasureSpec,heightMeasureSpec);
Log.e("测试","post: "+txt.getMeasuredWidth()+"---"+txt.getMeasuredHeight());
}
对于match_parent
来说,是无法进行测量的,因为此时无法知道父容器的剩余空间。
附录
View的MesaureSpec创建规则表