先来看一段代码:
@Override
public View getView(finalint position, ViewconvertView, ViewGroup parent) {
ViewHolder holder;
if (convertView== null) {
convertView = mInflater.inflate(R.layout.item, null);
holder = new ViewHolder();
holder.appIcon = (ImageView) convertView.findViewById(R.id.appIcon);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.size = (TextView) convertView.findViewById(R.id.size);
holder.chose = (CheckBox) convertView.findViewById(R.id.chose);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
MyFile myFile = fileList.get(position);
Drawable icon = myFile.icon;
if (icon!= null)
holder.appIcon.setImageDrawable(myFile.icon);
holder.chose.setChecked(myFile.isChose);
holder.name.setText(myFile.name);
holder.size.setText(myFile.formatSize);
holder.chose.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
fileList.get(position).isChose = b;
}
});
return convertView;
}
当你执行下去会发现出现图片错乱的问题,但是当你把if (icon != null)这段注释掉,你会发现突然好了,这是什么原因呢,下面就来分析一下:
a) ListView的复用机制
i. ListView是我们经常使用的一个控件,虽然说都会用,但是却并不一定完全清楚ListView的复用机制,虽然在Android 5.0版本之后提供了RecycleView去替代ListView和GridView,提供了一种插拔式的体验,也就是所谓的模块化。本篇主要针对ListView的复用机制进行探讨,因此就不提RecycleView。
ii. 首先需要说一下RecycleBin的基本原理,这个类也是实现复用的关键类。接着我们需要明确ActiveView的概念,ActiveView其实就是在UI屏幕上可见的视图(onScreenView),也是与用户进行交互的View,那么这些View会通过RecycleBin直接存储到mActivityView数组当中,以便为了直接复用,那么当我们滑动ListView的时候,有些View被滑动到屏幕之外(offScreen) View,那么这些View就成为了ScrapView,也就是废弃的View,已经无法与用户进行交互了,这样在UI视图改变的时候就没有绘制这些无用视图的必要了。他将会被RecycleBin存储到mScrapView数组当中,但是没有被销毁掉,目的是为了二次复用,也就是间接复用。当新的View需要显示的时候,先判断mActivityView中是否存在,如果存在那么我们就可以从mActivityView数组当中直接取出复用,也就是直接复用,否则的话从mScrapView数组当中进行判断,如果存在,那么二次复用当前的视图,如果不存在,那么就需要inflate View了。
iii . 可能很多读者都不太明白直接复用到底是怎么个过程,举个例子,比如说我们ListView一页可以显示10条数据,那么我们在这个时候滑动一个Item的距离,也就是说把position = 0的Item移除屏幕,将position = 10 的Item移入屏幕,那么position = 1的Item是不是就直接能够从mActivityView数组中拿到呢?这是可以的,我们在第一次加载Item数据的时候,已经将position = 0~9的Item加入到了mActivityView数组当中,那么在第二次加载的时候,由于position = 1 的Item还是ActivityView,那么这里就可以直接从数组中获取,然后重新布局。这里也就表示的是Item的直接复用。简单来说就是如果对应的position的item在当前显示的界面就相当于移动一下位置。
b) 上面是理论基础部分,接着结合代码分析一下,首先我们代码复用了“convertView”,这个就是起因,正如上面介绍的,“convertView”是来自于mScrapView或者mActivityView数组,因此每次滑动时最上面一个item会消失在屏幕中,最下面一个会出现在屏幕中。然后ListView会复用上一个消失的item,这个item的各种值此刻还是保留的,接着就是上面问题的解释了,为什么去掉了if (icon!= null)这段就不会图片错乱了。因为有这个条件我们当前的item就可能不会重新设置ImageView的图片,导致它还是上一次的图片
c) 因此有个结论,为了防止图片错乱,有两个办法,一个是基础部分,就是item的每个控件都要重新赋值,不管值是否为空;另一个就是设置Tag,这种是针对异步加载图片情况处理的