listview数据错乱、重复的原因分析与解决方案

目前我们使用listview展示数据时,adapter的getView方法通常使用convertView.setTag(viewHolder)的方式来避免出现卡顿的情况,这种方式能使convertView得以复用,避免重复的调用inflate方法渲染界面。但是,如果使用不当,可能会出现数据错乱、重复的问题,比如下面这个demo:

这个demo是要listview在偶数行只显示大写字母,在奇数行既显示大写字母又显示小写字母:

listview数据错乱、重复的原因分析与解决方案_第1张图片


第一屏的显示是没问题的,但是向下滑动几行,就会出现有问题了:

listview数据错乱、重复的原因分析与解决方案_第2张图片

J这一行按理说应该只显示大写字母,但是连小写字母也显示出来了,而且显示的居然还是a。

demo的getView代码:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    MyBean myBean = getItem(position);
    MyHolder holder;
    if(convertView == null){
        holder = new MyHolder();
        convertView = LayoutInflater.from(context).inflate(R.layout.item_listview, null);
        holder.tv_title = (TextView) convertView.findViewById(R.id.tv_title);
        holder.tv_subtitle = (TextView) convertView.findViewById(R.id.tv_subtitle);
        convertView.setTag(holder);
    }else{
        holder = (MyHolder) convertView.getTag();
    }
    holder.tv_title.setText(myBean.title);

    //注意这里,布局文件tv_subtitle的visibility是GONE
    if(position % 2 == 0){
        holder.tv_subtitle.setVisibility(View.VISIBLE);
        holder.tv_subtitle.setText(myBean.subTitle);
    }
    return convertView;
}


原因分析

通过setTag避免重复渲染,第一屏界面肯定没有问题,因为没有可复用的,但继续向下滑动时,就会出现复用了。下面这张图很好的说明了复用的情形:

listview数据错乱、重复的原因分析与解决方案_第3张图片

假设一屏只能显示7个item,当item滑动到item8时,adapter的getView方法就不会再去调用inflate方法渲染页面,而是直接复用item1的页面。

需要注意的是,在滑动时,只有当最上面的item完全消失后,下面刚出来的item才会复用它的convertView,如果二者能同时出现,比如当最上面的item消失了50%,最下面的item露出了10%,那么最下面的item复用的不是最上面的item,而是最上面的item再往上的那个item。(有点拗口,但我觉得说的很明白了,上面那张图只供理解参考)

结合上面demo的getView代码分析:
布局文件tv_subtitle的visibility是GONE,只有满足下面的条件才会显示出来:

if(position % 2 == 0){
   holder.tv_subtitle.setVisibility(View.VISIBLE);
   holder.tv_subtitle.setText(myBean.subTitle);
}

显示J 的这行position % 2是1,所以不会走到if里面,而这行复用了A的那行,它的convertView是A那行的convertView,而A的tv_subtitle的visibility是VISIBLE,所以J这行也就显示出了tv_subtitle。有人可能问了,第一屏截止到H,为什么I没有复用A,而是J复用了?这个上面已经解释过了,I出来的时候A那行还没完全消失,所以不会复用A。

解决方案

就上面的demo来说,就是加一个else:

if(position % 2 == 0){
    holder.tv_subtitle.setVisibility(View.VISIBLE);
    holder.tv_subtitle.setText(myBean.subTitle);
}else{
    holder.tv_subtitle.setVisibility(View.GONE);
}

因为convertView会复用,所以对ViewHolder里的每个组件,我们都要更新它的状态,如果不更新就可能会显示上一屏的数据。

你可能感兴趣的:(Android知识点)