在过兼容测试的时候,有个需求向下兼容到4.4版本,结果遇到闪退问题:
java.lang.ClassCastException: android.widget.FrameLayout$LayoutParams cannot be cast to android.widget.AbsListView$LayoutParams
查看异常栈的调用:
at android.widget.ListView.setupChild(ListView.java:1826)
at android.widget.ListView.makeAndAddView(ListView.java:1793)
at android.widget.ListView.fillDown(ListView.java:691)
at android.widget.ListView.fillGap(ListView.java:655)
发现它出错的代码在ListView的setUpChild中,于是查看setUpChild的代码:
可以看到这里强转出错了,也就是ListView中的每个ItemView的LayoutParams都必须是AbsListView.LayoutParams,代码顺着调用链往上看,即mkaeAndAddView方法看看:
可以发现传入setupChild的view是通过obtainView得来的,于是查看obtainView的代码(该方法在ListView的父类:AbsListView中实现):
在方法中调用了setItemViewLayoutParams(child, position)方法:
可以看到不论在setAdapter中的itemView的LayoutParams是什么类型,最终都一定被转为AbsListView.LayoutParams类型。但是为什么4.4的版本会报强转失败呢?去看看4.4的ListView源码,在obtainView方法中找到这个:
(链接:http://androidxref.com/4.4.4_r1/xref/frameworks/base/core/java/android/widget/AbsListView.java)
可以看到LayoutParams转化的条件是mAdapterHasStableIds,这个mAdapterHasStableIds是由mAdapter的hasStableIds决定的:
在BaseAdapter中默认返回false,回到看看代码中创建itemView的逻辑:
mFooterView = LayoutInflater.from(this).inflate(R.layout.footer_layout, this, false)
出错的原因是这个this并不是LisView,而是其他的View!
所以如果在4.4想解决这个问题: 在创建itemView时,这样创建(其实正常情况下都是这样创建的):
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
这里传入的parent为ListView:
这样就不会有错了。
为什么第2个参数为ListView就不会有错了?
可以跟进inflate的源码中看看:
可以看到通过root的generateLayoutParams方法获取了LayoutParams,并将值赋予了view的LayoutParams,因为ListView没有覆写这个方法,因此看看ListView的父类(AbsListView)的generateLayoutParams方法:
可以看到这个LayoutParams正是AbsListView.LayoutParams,所以自然不会报强转异常了!
总结
由于现在市面上几乎已经没有4.4的手机了,而且大多数时候创建通过LayoutInflater创建view时,都是在BaseAdapter的getView中创建,传入的第二个参数也是parent(即ListView),所以这个错误的出现是非常难得的,这里只是给出现问题的人提供解决方法。
|