在使用ListView時铝条,很多情況下由于某些數(shù)據(jù)的加載不能立刻完成邓馒,為了防止UI線程的阻塞我們通常會采用異步加載數(shù)據(jù)的方式碴巾。這時就會產(chǎn)生一個問題:數(shù)據(jù)加載錯位墙杯!
原因分析
造成ListView的數(shù)據(jù)加載錯位是由于我們同時使用convertView和異步加載引起的配并。
- 圖一:假設(shè)我們屏幕上一開始顯示了5個完整的Item,針對這5個Item convertView都為null高镐。
- 圖二:當(dāng)?shù)?個Item有一半移出屏幕溉旋,第6個Item有一半移入屏幕時,屏幕上顯示的Item條目數(shù)最多嫉髓。也即我們擁有6個不同的convertView观腊。
- 圖三:當(dāng)?shù)?個Item完全滑出屏幕之后,如果第7個Item進入屏幕算行,則此時會復(fù)用與第1個Item相關(guān)聯(lián)的convertView恕沫。
假設(shè)我們的第一個Item的數(shù)據(jù)加載需要較長的時間,我們采用了異步加載的方式纱意。想像一下當(dāng)我們已經(jīng)啟動了異步加載線程婶溯,但是在數(shù)據(jù)還沒有完全加載完成的時候,我們的ListView的狀態(tài)已經(jīng)由圖一變成了圖三。此時如果加載完畢迄委,由于數(shù)據(jù)加載線程并不知道ListView已經(jīng)發(fā)生了改變褐筛,依舊會將數(shù)據(jù)更新至與Item1相關(guān)聯(lián)的convertView上。此時Item7就會顯示Item1的數(shù)據(jù)!!這就是所謂的ListView加載錯位叙身。
問題解決
追根究底加載錯位的問題出在加載線程在數(shù)據(jù)加載完畢之后不知道ListView已經(jīng)發(fā)生了改變渔扎,從這一點考慮我們可以通過以下步驟進行解決:
- 在啟動加載線程之前給每個Item的控件設(shè)定與當(dāng)前Item關(guān)聯(lián)的Tag.
- 在數(shù)據(jù)加載完畢之后通過從控件中g(shù)etTag()的方法判斷是否為“正確”的關(guān)聯(lián)控件。
核心代碼實現(xiàn):
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
final ViewHolder viewHolder;
final TypeInfo typeInfo = getItem(position);
Log.d("MyAdapter", "convertView:"+convertView+" position="+position);
if(convertView == null){
v= mInflater.inflate(mLayoutId, parent, false);
viewHolder = new ViewHolder();
viewHolder.tv_title = (TextView)v.findViewById(R.id.title);
viewHolder.iv_pic = (ImageView)v.findViewById(R.id.pic);
v.setTag(viewHolder);
}
else{
v = convertView;
viewHolder = (ViewHolder)v.getTag();
}
viewHolder.tv_title.setText(typeInfo.typeTitle);
// ------1.不添加以下代碼則會造成圖片錯位
viewHolder.iv_pic.setTag(typeInfo.typeTitle);
// ------
if(position == 0){
viewHolder.iv_pic.setImageDrawable(new BitmapDrawable());
// 模擬網(wǎng)絡(luò)信轿、文件加載圖片的時間晃痴,這里假設(shè)加載用了3s
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//注意這里的typeInfo.typeTitle是在啟動線程的時候就已經(jīng)獲取到值了,而不是run到此處時才獲取到的值
Log.d("MyAdapter", "tag:"+typeInfo.typeTitle);
//運行到此處時模擬加載已經(jīng)完成
// ------2.不添加以下代碼則會造成圖片錯位
Log.d("MyAdapter","tag:"+viewHolder.iv_pic.getTag());
if(viewHolder.iv_pic.getTag() == null)
return;
if(!viewHolder.iv_pic.getTag().equals(typeInfo.typeTitle))//不是真正的pos0的imageView控件
return;//do nothing
// ------
viewHolder.iv_pic.setImageResource(typeInfo.typePic);
}
}, 3000);
}else{
viewHolder.iv_pic.setImageResource(typeInfo.typePic);
}
return v;
}