1. 程式人生 > >ListView setAdapter has already been called 異常

ListView setAdapter has already been called 異常

本文參考:http://blog.csdn.net/androiddevelop/article/details/8474939

異常提示:Java.lang.IllegalStateException: Cannot add header view to list -- setAdapter has already been called.

產生原因:addHeaderView()呼叫在setAdapter()之後,並且該程式碼執行在Android4.3之前的系統版本。Android SDKAPI Level對應表

我們來看看(Android4.3API-18 和 (Android4.2API-17Listview

程式碼片段:

API-18  

public void addHeaderView(View v, Object data, boolean isSelectable) {  
02.        final FixedViewInfo info = new FixedViewInfo();  
03.        info.view = v;  
04.        info.data = data;  
05.        info.isSelectable = isSelectable;  
06.        mHeaderViewInfos.add(info);  
07.   
08.        // Wrap the adapter if it wasn't already wrapped.  
09.        if (mAdapter != null) {  
10.            if (!(mAdapter instanceof HeaderViewListAdapter)) {  
11.                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);  
12.            }  
13.   
14.            // In the case of re-adding a header view, or adding one later on,  
15.            // we need to notify the observer.  
16.            if (mDataSetObserver != null) {  
17.                mDataSetObserver.onChanged();  
18.            }  
19.        }  
20.}  

API-17
public void addHeaderView(View v, Object data, boolean isSelectable) {  
02.   
03.        if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {  
04.            throw new IllegalStateException(  
05.                    "Cannot add header view to list -- setAdapter has already been called.");  
06.        }  
07.   
08.        FixedViewInfo info = new FixedViewInfo();  
09.        info.view = v;  
10.        info.data = data;  
11.        info.isSelectable = isSelectable;  
12.        mHeaderViewInfos.add(info);  
13.   
14.        // in the case of re-adding a header view, or adding one later on,  
15.        // we need to notify the observer  
16.        if (mAdapter != null && mDataSetObserver != null) {  
17.            mDataSetObserver.onChanged();  
18.        }  
19.}  

API-17中,假如adapter不為空,則會直接丟擲異常,而在API-18中則做了相關的優化。

再看一下setAdapter()方法部分原始碼:

 public void More ...setAdapter(ListAdapter adapter) {
            ...此處省略部分程式碼...
         if (mHeaderViewInfos.size() > 0||                mFooterViewInfos.size() > 0) {
          mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
          } else {
           mAdapter = adapter;
          }
        ...此處省略部分程式碼...
    }


建議

按照正常的人邏輯:先新增header,在4.3以前版本,由於此時mAdapter = null,所以不會走拋異常邏輯。再設定adapter,此時看adapter的原始碼,此時的adapter成為了HeaderListAdapter了,我們在此時可以移除掉header。

等在訪問網路成功之後,再去新增header,此時走進addHeader的原始碼,雖然if語句中mAdapter!=null條件滿足,但是後面條件mAdapter已經是HeaderListAdapter型別了,條件不滿足,也不會走拋異常邏輯。此時就是可行的。

總結:先新增header或者footer,在setAdapter之後再移除掉header或者footer。在訪問網路成功之後,再去新增header或者footer即可。