View的高度获取问题
在Android中一个View被创建后是不能够立刻知道其宽度和高度的。
如下代码试图在footerView创建后,通过其getWidth()和getHeight()方法得到其宽度和高度,实际得到的width和height都等于0。
View footerView = View.inflate(this, R.layout.list_footer, null);
int width = footerView.getWidth();
int height = footerView.getHeight();
这是因为,在Android中一个View在创建完成后还需要经过测量(measure),布局(layout)和绘制(draw)三个步骤才会显示出来。其宽度和高度通常只有在布局完成后才能够获取到,如果在布局之前就尝试获取其宽度和高度,只能得到0。
因此,如果要获取一个View的宽度和高度,通常可以等待其布局完成后再尝试获取。在Android中提供了一些监听事件,可以在布局完成,或绘制之前通知监听者。
如下代码均可以得到footerView的宽度和高度。
footerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
footerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int wight = footerView.getWidth();
int height =footerView.getHeight();
}
});
footerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
footerView.getViewTreeObserver().removeOnPreDrawListener(this);
int wight = footerView.getWidth();
int height =footerView.getHeight();
return false;
}
});
View高度的手动测量
虽然可以通过注册监听的方式来获取View的宽度和高度,但这时通常都已经完成了布局,如果我们需要在获取到View的宽度和高度后再对View的布局做额外的调整,要么是没有效果,要么是需要重新布局,影响效率,如果界面已经绘制完成再去调整布局,可能还会导致界面出现一些闪烁。因此,对这类需要根据某个View的宽度或高度做重新布局的时候,就需要在View创建完成后能够立刻得到其宽度和高度。要想实现这点,可以通过手动执行View的measure方法。示例代码如下。
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
footerView.measure(width, height);
int width = footerView.getMeasuredWidth();
int height =footerView.getMeasuredHeight();
RelativeLayout 执行measure方法异常
上述代码对根Layout为FrameLayout,LinearLayout的View都能够正常工作,但是对根Layout为RelativeLayout的View,可能会出现如下的NullPointerException异常。
问题原因
产生此异常的原因是RelativeLayout在Android4.4以下版本中的一个bug。此bug已在Android4.4版本中修正。以下是Android4.0和Android4.4版本RelativeLayout的部分源码对比。
可以看到在Android4.0版本中没有判断mLayoutParams是否为null,如果mLayoutParams为null就会抛出NullPointerException异常。而在Android4.4版本中增加了对mLayoutParams是否为null的判断。
解决方法
由于此bug只在RelativeLayout中存在,只需要在RelativeLayout布局外面再嵌套一层FrameLayout或者LinearLayout即可。