貼出源代碼:
android.widget.ListView
...
今天看見這個異常百思不得其解,幸好在論壇上看見一位牛人的解析,瞬間明了。
原文分析:
普通情況下。上述異常一般發生在我們啟動一個背景線程載入資料,同一時候在主線程(即UI線程)重新整理ListView在顯示新載入的内容。
我們的做法通常是:在背景線程中把載入的資料放入到一個List中,而在主線程中執行個體化Adapter,這個Adapter中所用到的List正是在背景線程中載入的那個List。
發生上述異常的代碼思路是這樣子的。請看代碼:
首先,我們定義一個List全局變量,背景線程中載入的資料就放到這個list中(請注意我标了紅色的list變量,問題就出在它身上):
private List<Map<String,Object>> list = null;
接着,我們會啟動一個背景線程,用于載入資料:
class GetDataThread implements Runnable{//單獨啟動一個線程用于載入歌曲清單
@Override
public void run() {
list = new ArrayList<Map<String,Object>>();
//然後把搜尋出來的資料放入到list中。
}
}
最後,我們會在主線程中重新整理界面。重新整理界面的代碼。我們是要放到handler中處理的:
class RefreshLocalMusicListThread implements Runnable{
@Override
public void run() {
local_lv = (ListView)findViewById(R.id.local_musiclist);
SimpleAdapter adapter = new SimpleAdapter(LocalActivity.this,list,R.layout.local_music_list,new String[] {"local_name","local_size"}, new int[]{R.id.local_name,R.id.local_size});
local_lv.setAdapter(adapter);
LocalActivity.this.registerForContextMenu(local_lv);
handler.postDelayed(refreshThread, 10);
}
}
以上的思路,是會發生上述異常的!
以下請看我的分析:
當執行 SimpleAdapter adapter = new SimpleAdapter(LocalActivity.this,list,R.layout.local_music_list,new String[] {"local_name","local_size"}, new int[]{R.id.local_name,R.id.local_size});時集合list中資料與我們的listView是綁定在一起的了。
此時。,假如list中的資料有5條,即list.size()==5,這時與listView綁定的就是5條資料。可是,我們的背景線程還在執行,list中的資料會發生變化,然而我們的listView認定的就是之前僅僅有5條資料的list,可是這時的list中的資料已經不是5條了。就是這個沖突導緻了上述的異常!!!發生在else
if()推斷語句處
網上有這樣一種解決方法(實際上解決不了問題):
在 adapter.notifyDataSetChanged() 之前調用listview.setVisibility(View.GONE);在adapter.notifyDataSetChanged() 之後調用listview.setVisibility(View.VISIBLE)
可是這是錯誤的!!
!
正确的解決方法是這種:
既然與listView綁定了的list發生了變化而沒來得及通知listView導緻了上述的異常,那我們就針對這一點,僅僅要listView與list綁定後,在listView顯示之前不要讓list發現變化即可了。做法有非常多種。我個人的做法是這樣子的:
首先,定義一個獨立的List:
private List<Map<String,Object>> data = null;
接着,在onCreate或者onResume中初始化它(當然,你也能夠在每次用到它的時候初始化它。隻是這樣子會初始化非常多對象,浪費記憶體,不推薦):
data = new ArrayList<Map<String,Object>>();
然後,在建立adapter之前,把list中資料放入到集合data中。注意千萬不要直接指派:data = list(這是錯誤的。由于這樣data也指向了list所在的記憶體位址,即data跟list是同一個對象。list改變的話data也跟着改變)。應該這麼做:
data.clear();//要先清空data中的資料。避免把list中的資料反複放入data中。
data.addAll(list);//這樣做。list中的資料就放入到data中,之後list在背景線程中改變,但data不會改變,這時,你再
SimpleAdapter adapter = new SimpleAdapter(LocalActivity.this,data,R.layout.local_music_list,new String[] {"local_name","local_size"}, new int[]{R.id.local_name,R.id.local_size});
listView與data綁定,就不會發生上述異常了!
總結來說。即建立一個緩存變量。存儲的值是第一次查詢得到資料 ,背景線程繼續查詢出來的資料不再使用。如此就保證了顯示資料時不會報出異常。