精选分析listview乱序的原因

最大的想法就是:在进行listview复用的时候,每一个item要进行特殊处理的时候,其他剩下的item也得进行相应的变化处理:三种方法:

                                 1.进行if和else设置处理
                                 2.设置tag将和对象进行一一绑定;
                                 3.设置标志位;


ListView 滑动时图片(或背景色)重复混乱

在讨论图片重复问题之前,先来看看背景色重复的问题:

想要达到的效果:品牌列表的前三项的背景色设置为砖红色,剩余其他项的背景色为默认色。

实际效果:滑动列表后,后面应该显示默认背景色的item也会出现砖红色背景

背景色重复的效果图如下:

有关ListView优化机制及滑动时数据错乱有关问题的讨论

通过代码来进一步分析,下面是处理列表数据显示的adapter类名为“TestDisorderListAdapter”继承BaseAdapter。brandInfoList是一个列表,包含了一个个BrandItemInfo对象(data modle):

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.aliao.myandroiddemo.adapter;  
  2.   
  3. import android.content.Context;  
  4. import android.util.Log;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.BaseAdapter;  
  9. import android.widget.CheckBox;  
  10. import android.widget.TextView;  
  11.   
  12. import com.aliao.myandroiddemo.R;  
  13. import com.aliao.myandroiddemo.domain.BrandItemInfo;  
  14.   
  15. import java.util.List;  
  16.   
  17. /** 
  18.  * Created by liaolishuang on 14-3-31. 
  19.  */  
  20. public class TestDisorderListAdapter extends BaseAdapter{  
  21.   
  22.     private Context context;  
  23.     private List<BrandItemInfo> brandInfoList;  
  24.     private final String TAG = "disorderlist";  
  25.   
  26.     public TestDisorderListAdapter(Context context, List<BrandItemInfo> list){  
  27.   
  28.         this.context = context;  
  29.         brandInfoList = list;  
  30.   
  31.     }  
  32.   
  33.     @Override  
  34.     public int getCount() {  
  35.         return brandInfoList.size();  
  36.     }  
  37.   
  38.     @Override  
  39.     public Object getItem(int i) {  
  40.         return null != brandInfoList?brandInfoList.get(i):null;  
  41.     }  
  42.   
  43.     @Override  
  44.     public long getItemId(int i) {  
  45.         return i;  
  46.     }  
  47.   
  48.     private class ViewHolder{  
  49.         private TextView brandEnNameTv;  
  50.         private TextView brandChNameTv;  
  51.         private CheckBox followCheckBox;  
  52.     }  
  53.   
  54.     @Override  
  55.     public View getView(int i, View view, ViewGroup viewGroup) {  
  56.   
  57.         ViewHolder viewHolder = null;  
  58.   
  59.         if(null == view){  
  60.               
  61.             LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  62.             view = inflater.inflate(R.layout.item_testdisorderitem,null);  
  63.   
  64.             viewHolder = new ViewHolder();  
  65.             viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);  
  66.             viewHolder.brandEnNameTv= (TextView) view.findViewById(R.id.item_enName_txt);  
  67.   
  68.             view.setTag(viewHolder);  
  69.   
  70.         }else {  
  71.              
  72.             viewHolder = (ViewHolder) view.getTag();  
  73.         }  
  74.   
  75.         BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);  
  76.         viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());  
  77.         viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());  
  78.   
  79.         if(i < 3){  
  80.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  81.         }  
  82.          
  83.         return view;  
  84.     }  
  85.   
  86. }  

为了设置品牌列表的前三项的背景色,在getView()方法中加入代码

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. if(i < 3){  
  2.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  3.         }  

i是代表每一行的position,由0开始。当向上滑动列表时,i的值随着滑动行的改变而递增,没有任何问题,但是为什么只有i<3的情况才改变颜色,其他项还会有背景色的改变呢?

在Adapters and Holder Pattern部分我们已经了解了listview的缓存优化机制,滚出屏幕的视图会被缓存下来并被复用。我们以为只要设置i<3就可以让前3项变色,其他项自然就是背景色,却忽略了,除了前三项之外的某些行会去复用前三项的视图因此也就会有相同的背景色。所以当i>=3对应的某些行由于复用了i<3对应的行,造成了背景色也为砖红色。

解决方法就是必须对i不小于3的情况进行处理,把它设置为背景色即:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. if(i < 3){  
  2.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  3.         }else{  
  4.             view.setBackgroundColor(context.getResources().getColor(R.color.background));  
  5.         }  
对于图片重复的问题,其实是一样的道理,举这么个例子:

评论列表中会显示头像和评论内容,从手机接口端读取评论内容,其中的一个字段是用户头像的下载地址,因为有的用户有上传头像有的没有,如果用户没有上传头像,那么返回的用户头像的字段为空字符串。在getView()里面就需要判断当用户头像不为空字符串的时候去设置头像,否则为默认头像,该默认头像由应用程序本地存储的图片,在xml文件的ImageView里设置了。正确的代码片段为:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.        if(!brandItemInfo.getBrandImage().equal("")){  
  2.            //根据url loadImage  
  3.        }else{  
  4.            viewHolder.brandLogo.setImageResource(R.drawable.ic_default_head);  
  5.        }         
如果不进行else处理,那么原先显示默认头像的imageView在list滑动后会重复显示其他头像的图片。

ListView滑动后出现checkbox选取错位

有关ListView优化机制及滑动时数据错乱有关问题的讨论

我遇到过的checkbox选取错位是列表上有checkbox,选取某个checkbox为选中状态,然后滑动列表发现其他未选择过的checkbox也变成了选中状态。重现该问题只需要在item的xml file中加入checkbox,然后java代码里不对checkbox做任何处理,测试时直接选中某个checkbox,滑动列表就会看到某些行会重复出现checkbox被选中,这种情况听起来和上一节所说的listview缓存优化问题引起重复错乱如出一辙(当然这只是我重现问题最简单的方式)。但是之所以把checkbox单独拿出来说是因为他与上述的问题并不完全相同,它涉及到列表项上的某个view状态的改变会影响到与该列表项对应的数据对象的改变。滑动list后checkbox选中状态出现问题,是由于rowview和对象一一对应,对象里的状态值没有相应改变。

在列表上像checkbox、toglebutton这种控件,它的选取状态会影响到数据的变化。例如在列表项上的checkbox,如果是选中后,该列表项对应的对象里的数据就会发生改变。这就涉及到数据模型与列表项之间的"通讯",当列表项上的checkbox状态改变,要相应地修改改行对应对象的数据。

The row can also containviews which interact with the underlying data model via the adapter. For example, you can have aCheckbox in your row layout and if theCheckbox is selected, the underlying data is changed.

Frequently you need to select items in yourListView. As the row of theListView are getting recycled you cannot store the selection on theView level.

To persist the selection you have to update your data model with the selected state.

To update the data model in yourListView you define your ownAdapter class. In this adapter class you attach a listener to theView which is responsible for selecting the model element. If selected you update the state in the model which you can add as a tag to the View to have access to it.”

我们在处理listview带有像checkbox,toglebutton这类控件的时候,需要监听控件的状态,一旦状态发生改变,就去改变列表项对应的对象数据。通过setTag()方法把checkbox与对象绑定在一起,一旦状态改变就在监听方法里通过getTag()方法将对象取出,更改对象中选中字段的状态值。

代码实现:

domain中的实体类BrandItemInfo类:

增加了isSelected变量用来保存checkbox的选取状态

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.aliao.myandroiddemo.domain;  
  2.   
  3. /** 
  4.  * Created by liaolishuang on 14-3-31. 
  5.  */  
  6. public class BrandItemInfo {  
  7.   
  8.     private String brandEnName;  
  9.     private String brandChName;  
  10.     //    private String brandImage;  
  11.     private int brandImage;  
  12.     private boolean selected;  
  13.   
  14.     public boolean isSelected() {  
  15.         return selected;  
  16.     }  
  17.   
  18.     public void setSelected(boolean selected) {  
  19.         this.selected = selected;  
  20.     }  
  21.   
  22.     public void setBrandImage(int brandImage) {  
  23.         this.brandImage = brandImage;  
  24.     }  
  25.   
  26.     public void setBrandChName(String brandChName) {  
  27.         this.brandChName = brandChName;  
  28.     }  
  29.   
  30.     public void setBrandEnName(String brandEnName) {  
  31.         this.brandEnName = brandEnName;  
  32.     }  
  33.   
  34.     public String getBrandChName() {  
  35.         return brandChName;  
  36.     }  
  37.   
  38.     public String getBrandEnName() {  
  39.         return brandEnName;  
  40.     }  
  41.   
  42.     public int getBrandImage() {  
  43.         return brandImage;  
  44.     }  
  45.   
  46. }  

Adapter类:

将每行的checkbox与该行相对应的brandItemInfo对象通过setTag()方法关联起来。并增加了checkbox的监听,一旦监听到选取状态的改变,就通过getTag()方法取出对象来更新brandItemInfo对象的isSelected字段的值。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.aliao.myandroiddemo.adapter;  
  2.   
  3. import android.content.Context;  
  4. import android.util.Log;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.BaseAdapter;  
  9. import android.widget.CheckBox;  
  10. import android.widget.CompoundButton;  
  11. import android.widget.ImageView;  
  12. import android.widget.TextView;  
  13.   
  14. import com.aliao.myandroiddemo.R;  
  15. import com.aliao.myandroiddemo.domain.BrandItemInfo;  
  16.   
  17. import java.util.List;  
  18.   
  19. /** 
  20.  * Created by liaolishuang on 14-3-31. 
  21.  */  
  22. public class TestDisorderListAdapter extends BaseAdapter{  
  23.   
  24.     private Context context;  
  25.     private List<BrandItemInfo> brandInfoList;  
  26.     private final String TAG = "disorderlist";  
  27.   
  28.     public TestDisorderListAdapter(Context context, List<BrandItemInfo> list){  
  29.   
  30.         this.context = context;  
  31.         brandInfoList = list;  
  32.   
  33.     }  
  34.   
  35.     @Override  
  36.     public int getCount() {  
  37.         return brandInfoList.size();  
  38.     }  
  39.   
  40.     @Override  
  41.     public Object getItem(int i) {  
  42.         return null != brandInfoList?brandInfoList.get(i):null;  
  43.     }  
  44.   
  45.     @Override  
  46.     public long getItemId(int i) {  
  47.         return i;  
  48.     }  
  49.   
  50.     private class ViewHolder{  
  51.         private TextView brandEnNameTv;  
  52.         private TextView brandChNameTv;  
  53.         private CheckBox followCheckBox;  
  54.         private ImageView brandLogo;  
  55.     }  
  56.   
  57.   
  58.     @Override  
  59.     public View getView(int i, View view, ViewGroup viewGroup) {  
  60.   
  61.         ViewHolder viewHolder = null;  
  62.         BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);  
  63.   
  64.         if(null == view){  
  65.             
  66.             LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  67.             view = inflater.inflate(R.layout.item_testdisorderitem,null);  
  68.   
  69.             viewHolder = new ViewHolder();  
  70.             viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);  
  71.             viewHolder.brandEnNameTv = (TextView) view.findViewById(R.id.item_enName_txt);  
  72.             viewHolder.brandLogo = (ImageView) view.findViewById(R.id.item_brandLogo_imagev);  
  73.             viewHolder.followCheckBox = (CheckBox) view.findViewById(R.id.item_follow_checkbox);  
  74.             final ViewHolder finalViewHolder = viewHolder;  
  75.             viewHolder.followCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  76.                 @Override  
  77.                 public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  78.                     BrandItemInfo info = (BrandItemInfo) finalViewHolder.followCheckBox.getTag();  
  79.                     info.setSelected(compoundButton.isChecked());  
  80.                 }  
  81.             });  
  82.             view.setTag(viewHolder);  
  83.             viewHolder.followCheckBox.setTag(brandItemInfo);  
  84.              
  85.         }else {  
  86.              
  87.             viewHolder = (ViewHolder) view.getTag();  
  88.             viewHolder.followCheckBox.setTag(brandItemInfo);  
  89.         }  
  90.   
  91.         viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());  
  92.         viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());  
  93.         viewHolder.brandLogo.setImageResource(brandItemInfo.getBrandImage());  
  94.         viewHolder.followCheckBox.setChecked(brandItemInfo.isSelected());  
  95.   
  96.         return view;  
  97.     }  
  98.   
  99. }  


好文分享:

Lars Vogel的

Using lists in Android(ListView) 对ListView的使用做了非常系统完整的讲解,由浅到深很适合阅读。阅读了这篇文章后,对ListView的缓存优化机制有了更进一步的理解,从而有了本篇blog的Adapter and Holder Pattern作为阅读后的总结。

农民伯伯的:

ListView性能优化之视图缓存 介绍了Google I/O提供的优化Adapter方案,并对这些方案进行了测试。

ListView性能优化之视图缓存续 介绍了新浪微博中主界面的做法及测试数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值