接着上次的问题,已介绍过,在初始化或说OnCreate方法中获得加载的布局的宽高,最后说到,调用view.measure(0, 0);然后在调用getMeasuredWidth和getMeasuredHeight就能够取得丈量的宽高。可以参考:Android如何在初始化的时候获得加载的布局的宽高
今天在写类似的效果时,给ListView加载1个头部视图,通过listView$addHeadView添加到ListView的头部。为了描写起来简单起见,这个头部视图的布局我做了简化为下面:headview.xml
<?xml version="1.0" encoding="utf⑻"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/information_top" />
</RelativeLayout>
然后在初始化的时候想获得该布局的宽高,就这样做了:
//布局加载器
private LayoutInflater inflater;
//头部的View
private View headView;
//头部View的宽
private int headViewWidth;
//头部View的高
private int headViewHeight;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headView = inflater.inflate(R.layout.headview, null);
headView.measure(0, 0);
headViewWidth = headView.getMeasuredWidth();
headViewHeight = headView.getMeasuredHeight();
Log.i(TAG, "headViewWidth-->" + headViewWidth + " headViewHeight-->" + headViewHeight);
addHeaderView(headView);
结果。。。。。
程序崩溃了!尼玛,不是说好的headView.measure(0, 0);调用完就可以获得到布局的宽高了嘛,怎样这句好报错空指针异常了?
然后把布局修改成线性布局
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/information_top" />
</LinearLayout>
居然就行了。。。
也获得到了丈量的宽高:
接下来又开始了对问题的研究。为了简化问题,还是依照上次的此路。在上次的基础上添加布局viewgroup_relativelayout.xml,其实就是将外层的LinearLayout修改成RelativeLayout了,其他地方未变。加载该布局,然后丈量宽高。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<View
android:layout_width="50dp"
android:layout_height="50dp"
/>
</RelativeLayout>
看结果:
View,textview和线性布局的ViewGroup和上次的结果1样。但是将相对布局的ViewGroup直接挂掉。。刚开始还是特别的不爽的,为何常常遇到问题,不过1想,解决问题的进程就是水平提高的进程,所以打起精神进行研究。嘿嘿~
通过比较发现RelativeLayout出错了,所以很容易将空指针异常的地方定位在RelativeLayout的onMeasure函数中。
但是这个函数比较复杂了。两百多行代码,里面牵扯了很多的域变量和函数,读起来挺累的。因此在网上搜了好久,在eoe上面看到有人说了下面的方法:
如果使用Inflater的情况下会出现以上毛病,缘由是用Inflater渲染组件的时候并没有给其指定父控件,所以渲染器不会去解析width 和 height属性,就会致使空指针异常。
解决方法:
view.setLayoutParams(newLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));指定宽度和高度,然后调用measure方法就OK。
其实这个说法前面就研究过,确切是这模样的,但是LinearLayout为何没出问题RelativeLayout就出问题了呢,所以还是没有说明白缘由在哪里。
我尝试了1下确切弄定了,缘由尚不清楚,那就肯定需要查源代码了。在这个指点下给我提供了方向就是极可能在onMeasure方法里面调用了该相对布局的参数mLayoutParams,由于本来为null,那末调用的话就会报告空指针异常!
接下来就在源码的onMeasure函数里面将所有出现mLayoutParams的地方打上断点。然后断点调试
RelativeLayou类(我查看的是4.2的源码)
//493行
if (mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
//523行
if (mLayoutParams.height >= 0) {
height = Math.max(height, mLayoutParams.height);
}
果然mLayoutParams变量不存在为null。
到现在为止问题就清楚了。由于调用headView.measure(0, 0);的时候是通过inflate(R.layout.headview, null);方式加载的布局,因此设置的外层RelativeLayout布局的LayoutParams是null的,恰巧相对布局没有检查是不是为null就直接调用了mLayoutParams.width或mLayoutParams.height,因此就报空指针毛病了。这多是1个bug吧我认为。
既然知道了问题是出在了这里了,那末也很容易解决了。
最少有以下的解决方法:
1,加载布局的时候通过inflater.inflate(R.layout.viewgroup_relativelayout,(ViewGroup) findViewById(R.id.mainLayout),false);方式加载。这样的话会设置他的布局参数。
2 手动设置布局参数添加上
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
setLayoutParams(lp);
现在再来看上1篇博客里面讲的这个方法:
private void measureView(View child) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
if(lp == null){
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// child.setLayoutParams(lp);
}
//headerView的宽度信息
int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int childMeasureHeight;
if(lp.height > 0){
childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
} else {
childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);//未指定
}
//将宽和高设置给child
child.measure(childMeasureWidth, childMeasureHeight);
}
看起来貌似就是为了不外层布局是RelativeLayout的情况对LayoutParams做了判断!但是直接调用上面这个方法依然不行,由于new出来的LayoutParams没有设置进去,所以还需要调用child.setLayoutParams(lp);然后就弄定了。经过测试ok!
回到博客开始在ListView添加头View所遇到的问题也就很容易解决了。
1,将相对布局换成线性布局。
2,依然使用相对布局,使用上面说的两种方法设置外层的相对布局的布局参数。比如使用下面的方法,在家在布局的时候带上父布局,结果弄定!好开心
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headView = inflater.inflate(R.layout.headview, this,false);
headView.measure(0, 0);
//换成方法measureView (headView)也能够
headViewWidth = headView.getMeasuredWidth();
headViewHeight = headView.getMeasuredHeight();
Log.i(TAG, "headViewWidth-->" + headViewWidth + " headViewHeight-->" + headViewHeight);
addHeaderView(headView);
将headView.measure(0, 0);换成measureView (headView);也能够的。
但是加载布局的时候不带上父布局,调用的是headView =inflater.inflate(R.layout.headview, null);即便用下面代码:
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headView = inflater.inflate(R.layout.headview, null);
//headView.measure(0, 0);
measureView(headView);
headViewWidth = headView.getMeasuredWidth();
headViewHeight = headView.getMeasuredHeight();
Log.i(TAG, "headViewWidth-->" + headViewWidth + " headViewHeight-->" + headViewHeight);
addHeaderView(headView);
程序居然又崩溃了。看调试结果:
不过不用担心,这时候的毛病不是空指针了,而是类转换异常,相信通错误误很容易修改了
lp= new AbsListView.LayoutParams(AbsListView.LayoutParams.FILL_PARENT,AbsListView.LayoutParams.WRAP_CONTENT);
然后在运行就行了,大功告成。最后的忠告就是需要自己需要丈量布局宽高的时候慎用RelativeLayout,犹如解决ScrollView和ListView冲突的时候1种解决方法,这里我们只讨论其中1个冲突就是:致使ListView的高度显示不完全,出现只显示部份内容的情况。解决方法就是就是丈量ListView的总高度,然后设置1下,关键代码以下。试想,如果这时候候如果各个item是RelativeLayout的话,肯定会出错。
private void setListViewHeight(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
最后,这些东西可能有些人写程序的时候都从没遇到过,固然也不影响写出漂亮的APP。但是我认为不论是甚么问题,都能锻炼解决问题的能力,都是有助于学习。
文字表达能力太差,表述能力是硬伤啊!