ListView数据错乱--分析及解决

该篇内容主要是记录我在实际开发中遇到的ListView滑动时数据错乱的几种情况,以及解决方法。在进行ListView滑动时数据错乱问题讨论之前会对ListView所谓的<优化>进行说明。文章末尾分享了几篇文章,增强对ListView使用以及Adapter优化的理解,其中有对adapter优化方法的耗时测试的介绍。

Getting Start

遇到过的ListView在滑动时数据错乱的几种情况

  • Listview滑动后,图片(/背景色)重复混乱(非异步加载时)
  • Listview选取checkbox后,再滑动时,出现checkbox选取错位问题
  • ListView异步加载图片时,图片显示重复错乱

这里所说的"重复混乱"是指:在滑动list的时候,会看到某行本不该显示却重复显示了其他行的数据(根据情况的不同,数据可以是文本,checkbox的选中状态,图片,背景色等等...),而之所以让人感觉到混乱或者说错乱是因为这些item的重复现象有时候看似没有什么规律可寻。

在进行问题重现之前,先有必要对处理数据与视图显示的adapter类以及ViewHolder模式进行深入理解:

Adapters and Holder Pattern

adapter是数据与listview视图显示之间的桥梁:

“An adapter manages the data model and adapts it to the individual rows in the list view. An adapter extends theBaseAdapter class.

The adapter would inflate the layout for each row in itsgetView() method and assign the data to the individual views in the row.

The adapter is assigned to theListView via thesetAdapter method on theListView object.”

adapter中最重要的方法非getView()莫属,listview每一行的显示都会调用getView()方法,通过getView()方法将每一行要显示的数据指定给相应的view。

getView()通常的写法如下:

  1. private class ViewHolder{  
  2.        private TextView brandEnNameTv;  
  3.        private TextView brandChNameTv;  
  4.        private CheckBox followCheckBox;  
  5.    }  
  6.   
  7.    @Override  
  8.    public View getView(int i, View convertView, ViewGroup viewGroup) {  
  9.   
  10.        ViewHolder viewHolder = null;  
  11.   
  12.        if(null == convertView){  
  13.            
  14.            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  15.            convertView = inflater.inflate(R.layout.item_testdisorderitem,null);  
  16.   
  17.            viewHolder = new ViewHolder();  
  18.            viewHolder.brandChNameTv = (TextView) convertView.findViewById(R.id.item_chName_txt);  
  19.            viewHolder.brandEnNameTv= (TextView) convertView.findViewById(R.id.item_enName_txt);  
  20.   
  21.            convertView.setTag(viewHolder);  
  22.   
  23.        }else {  
  24.             
  25.            viewHolder = (ViewHolder) convertView.getTag();  
  26.        }  
  27.   
  28.        //set data  
  29.   
  30.        return convertView;  
  31.    }  
 private class ViewHolder{
        private TextView brandEnNameTv;
        private TextView brandChNameTv;
        private CheckBox followCheckBox;
    }

    @Override
    public View getView(int i, View convertView, ViewGroup viewGroup) {

        ViewHolder viewHolder = null;

        if(null == convertView){
          
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.item_testdisorderitem,null);

            viewHolder = new ViewHolder();
            viewHolder.brandChNameTv = (TextView) convertView.findViewById(R.id.item_chName_txt);
            viewHolder.brandEnNameTv= (TextView) convertView.findViewById(R.id.item_enName_txt);

            convertView.setTag(viewHolder);

        }else {
           
            viewHolder = (ViewHolder) convertView.getTag();
        }

        //set data

        return convertView;
    }
                                                                          代码片段1.1

以上这种写法listview进行了优化,对比与以下这种方式:

  1.  @Override  
  2. public View getView(int i, View view, ViewGroup viewGroup) {  
  3.   
  4.     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  5.     View rowview = inflater.inflate(R.layout.item_testdisorderitem,null);  
  6.   
  7.     TextView brandChNameTv = (TextView) rowview.findViewById(R.id.item_chName_txt);  
  8.     TextView brandEnNameTv= (TextView) rowview.findViewById(R.id.item_enName_txt);  
  9.   
  10.     //set data  
  11.       
  12.     return rowview;  
  13. }  
     @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowview = inflater.inflate(R.layout.item_testdisorderitem,null);

        TextView brandChNameTv = (TextView) rowview.findViewById(R.id.item_chName_txt);
        TextView brandEnNameTv= (TextView) rowview.findViewById(R.id.item_enName_txt);

        //set data
        
        return rowview;
    }
                                                                          代码片段1.2

具体是怎么优化的以及优化了什么呢?

从xml布局文件里inflate的每一个view都会产生一个java对象(eg:View view):

View view = inflater.inflate(R.layout.item_testdisorderitem, null);

inlating 布局文件和创建java对象对时间和内存的消耗都是昂贵的。

除此之外,使用findViewById()方法也相对地耗时。

为了让listview减少在时间和内存上的消耗,Android提供了convertView参数(getView方法的第二个参数,不一定都叫convertView,个人认为叫rowView更好)来实现这一优化。当用户滑动列表时,原先可视的item被滚出屏幕变得不可视,而代表该行的java对象可以被新的可视行复用。也就是说如果列表在手机屏幕中一屏可见的行有7行,当第一行滑出屏幕时,底部新滑出来的第8行可以复用第1行的java对象(即通过item布局inflate出来的view),Android已经把第一行的布局缓存起来,作为可以复用的rowview:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值