天天看點

ListView IllegalStateException

貼出源代碼:

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綁定,就不會發生上述異常了!

總結來說。即建立一個緩存變量。存儲的值是第一次查詢得到資料 ,背景線程繼續查詢出來的資料不再使用。如此就保證了顯示資料時不會報出異常。

上一篇: GridView
下一篇: Listview優化