【学习资料】android-list view各种奇葩问题总结
发布时间:2015年08月27日 15:37分 来源:博为峰教育网采编 关键词:Android,问题 | 上一篇 | 下一篇 |
对于list view,大家都想必又爱又恨,基于list view布局,只要有创造力就可以写出各种性能良好,界面酷炫的动画的list view。因此也会遇到一些问题。因此博为峰教育在此把问题总结一下。
一、scrollview嵌入list view,这种布局很是奇怪,我们平常开发的时候list view超过一屏时list view自动就会滚动,因为他自带了滚动条,不信的话可以去查看源码。呵呵,可能是习惯上的思维吧,scrollview放入一个listview,这时候list view就会显示一行。这是为什么?ScrollView中嵌套ListView空间,无法正确的计算ListView的大小,故可以通过代码,根据当前的ListView的列表项计算列表的尺寸。当然,网上有各种解决方法。我觉的比较好的两种方法。
(1)重写list view的ononMeasure(),效果还可以,不过有时候在我的机上测试时失败过。
public class MainListView extends ListView{ public MainListView(Context context) { // TODO Auto-generated method stub super(context); } public MainListView(Context context, AttributeSet attrs) { // TODO Auto-generated method stub super(context, attrs); } public MainListView(Context context, AttributeSet attrs, int defStyle) { // TODO Auto-generated method stub super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
(2)重新计算list view的item的height,累加起来就是list view的总高度。
public static void setListViewHeightBasedOnChildren(ListView listView) { // 获取ListView对应的Adapter ListAdapter listAdapter = listView.getAdapter(); if(listAdapter == null) { return; } int totalHeight = 0; for(int i = 0, len = listAdapter.getCount(); i < len; i++) { // listAdapter.getCount()返回数据项的数目 View listItem = listAdapter.getView(i, null, listView); listItem.measure(0, 0); // 计算子项View 的宽高 totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度 } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); // listView.getDividerHeight()获取子项间分隔符占用的高度 // params.height最后得到整个ListView完整显示需要的高度 listView.setLayoutParams(params);
这两个方法其实就是重新测量list view的宽度和高度。
二、Android ListView CheckBox状态错乱
很多人给出的两种解决办法
1:上来就说是因为convertview对象共用的原因,不能用convetView,而是每次getView()的时候都new一个对象的view出来.这种办法大概是用屁股想出来的.
2:即然错乱,那我就自己再弄一个集合保存checkBox的状态,再错乱,弄死你.即然adapter里有一个list集合里保存checkBox的状态了,为什么还要自己再保存一次checkBox的状态呢,不是多此一举吗?
为什么会产生这种现象呢?
1:首先分析下viewHolder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener()…);
这句话,就是给checkBox添加一个监听器,如果checkBox的状态改变了,那么系统就会自动回调里面的onCheckedChange()方法.
而文中onCheckedChange()方法里写的是记录这次改变后checkBox状态的代码.
2:再接着分析下if(list.get(position).type == A.TYPE_CHECKED){…},这部分代码是根据list集合里的对象属性初始化view里checkBox是否应该是选择状态.
3:我上下滑动listView的时候,checkBox的状态就错乱了,根据第二点的分析,无论如何checkBox的状态都不会错乱,除非list集合里对象的属性已经被改变了,到底是什么地方改变了它?
4:文中只有一个地方写了改变list集合里checkBox对象属性的地方,那就是第一点里提到的OnCheckedChangeListener()方法。它被执行了?
怎么回事,不可能吧,打个断点,跑一下便知上下滑动listView的时候确实停了下来.
5:这便是convertView的功能,因为不管listView里显示多少条数据,都只是共用那么几个对象,然后我们的代码每一次把得到的对象重新赋值而已。
终极解决方案:只要把添加监听器的方法加到初始化view中checkBox状态的代码之前即可.
三:Android ListView异步加载图片错位、重复、闪烁分析
比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而ImageView控件就是View对象通过findViewById()获得的,而我们在复用View对象时,同时这个ImageView对象也被复用了。比如第11个Item的View复用了第1个Item View对象,那么ImageView就同时被复用了,所以当图片没下载出来,这个ImageView(第11个Item)显示的数据就是复用(第1个Item)的数据。
1:Item图片显示重复
这个显示重复是指当前行Item显示了之前某行Item的图片。
比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中ListView已经滑动到了第14行,且滑动过程中该图片加载结束。第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的View对象可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。
2. Item图片显示错乱
这个显示错乱是指某行Item显示了不属于该行Item的图片。
跟上面的原因一样。
3. Item图片显示闪烁
上面介绍的另外一种情况,如果第14行图片又很快加载结束,所以我们看到第14行先显示了复用的第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。
解决方案:
通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行Item的标识是否一致,一致则显示,否则不做处理即可。
原理:首先给ImageView设置一个Tag,这个Tag中设置的是图片的url,然后在加载的时候取得这个url和要加载那position中的url对比,如果不相同就加载,相同就是复用以前的就不加载了。