接着上次的问题,已经介绍过,在初始化或者说OnCreate方法中获取加载的布局的宽高,最后说到,调用view.measure(0, 0);然后在调用getMeasuredWidth和getMeasuredHeight就可以获得测量的宽高。可以参考:Android如何在初始化的时候获取加载的布局的宽高
今天在写类似的效果时,给ListView加载一个头部视图,通过listView$addHeadView添加到ListView的头部。为了描述起来简单起见,这个头部视图的布局我做了简化为下面:headview.xml

<?xml version="1.0" encoding="utf-8"?><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;//头部的Viewprivate 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-8"?><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和上次的结果一样。但是将相对布局的ViewGroup直接挂掉。。刚开始还是特别的不爽的,为什么经常遇到问题,不过一想,解决问题的过程就是水平提高的过程,所以打起精神进行研究。嘿嘿~

通过比较发现RelativeLayout出错了,所以很容易将空指针异常的地方定位在RelativeLayout的onMeasure函数中。

但是这个函数比较复杂了。两百多行代码,里面牵扯了很多的域变量和函数,读起来挺累的。因此在网上搜了好久,在eoe上面看到有人说了下面的方法:

如果使用Inflater的情况下会出现以上错误,原因是用Inflater渲染组件的时候并没有给其指定父控件,所以渲染器不会去解析width 和 height属性,就会导致空指针异常。

解决方法:

view.setLayoutParams(newLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));指定宽度和高度,然后调用measure方法就OK。

其实这个说法前面就研究过,确实是这样子的,但是LinearLayout为什么没出问题RelativeLayout就出问题了呢,所以还是没有说明白原因在哪里。

我尝试了一下确实搞定了,原因尚不清楚,那就肯定需要查源代码了。在这个指导下给我提供了方向就是很可能在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,因此就报空指针错误了。这可能是一个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);


现在再来看上一篇博客里面讲的这个方法:

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);//未指定}//将宽和高设置给childchild.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,如同解决ScrollViewListView冲突的时候一种解决方法,这里我们只讨论其中一个冲突就是:导致ListView的高度显示不完整,出现只显示部分内容的情况。解决方法就是就是测量ListView的总高度,然后设置一下,关键代码如下。试想,如果这时候如果各个itemRelativeLayout的话,肯定会出错。

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。但是我认为不管是什么问题,都能锻炼解决问题的能力,都是有助于学习。

文字表达能力太差,表述能力是硬伤啊!囧…刚开始写博客,感谢大家围观。



测试加载各种类型布局的宽高参数的Demo和ListView添加头视图为相对布局的Demo

点此下载代码








更多相关文章

  1. kotlin实现Android(安卓)MVP模式开发
  2. Android线性布局和相对布局的详解和区别
  3. Android(安卓)多语言适配
  4. Android的repaint函数
  5. 使用NDK移植开源项目,JNI的使用技巧
  6. Android动态界面开发框架Tangram使用完整教程
  7. android周记1
  8. eclipse项目导入android studio 各类问题及解决方法
  9. Android之Tab分页标签的实现方法一-----TabActivity和TabHost的

随机推荐

  1. 【Android 内存优化】使用 Memory Analyz
  2. Android笔记-MultiThreading in Android(
  3. Android 内存管理 &Memory Leak & OOM 分
  4. Android与IOS的优缺点比较
  5. Android的配置文件操作的完美封装(使用注
  6. Android & iOS 开发全面对比分析
  7. Google发布App开发准则 为自主Android平
  8. Android之联系人处理
  9. Android的assets文件夹资源访问
  10. Android的UI显示原理总结