1. 程式人生 > >從原始碼上分析ListView的addHeaderView和setAdapter的呼叫順序

從原始碼上分析ListView的addHeaderView和setAdapter的呼叫順序

ListView想要新增headerview的話,就要通過addHeaderView這個方法,然後想要為ListView設定資料的話,就要呼叫setAdapter方法了。但是,在呼叫addHeaderView和setAdapter的順序上,有時會爆出java.lang.IllegalStateException: Cannot add header view to list -- setAdapter has already been called.的異常。這是因為我們在addHeaderView之前呼叫了setAdapter。所以,在這裡,建議setAdapter需要在addHeaderView和addfooterView之後呼叫。這樣就安全了。下面,我們來看看原始碼吧。看看究竟是什麼原因造成的。

Android-18(4.3)的addHeaderView原始碼:

public void addHeaderView(View v, Object data, boolean isSelectable) {
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mHeaderViewInfos.add(info);
 
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
 
            // In the case of re-adding a header view, or adding one later on,
            // we need to notify the observer.
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
}

Android-17(4.2)的addHeaderView的原始碼:
public void addHeaderView(View v, Object data, boolean isSelectable) {
 
        if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
            throw new IllegalStateException(
                    "Cannot add header view to list -- setAdapter has already been called.");
        }
 
        FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mHeaderViewInfos.add(info);
 
        // in the case of re-adding a header view, or adding one later on,
        // we need to notify the observer
        if (mAdapter != null && mDataSetObserver != null) {
            mDataSetObserver.onChanged();
        }
}
在上面,我們可以對比出程式碼中的處理的不同。在17版本中,只要adapter不為空的話,那就直接會丟擲異常,而這個異常恰好就是我們文章開頭說到的異常。在18版本中,如果adapter不為空的話,則會新建一個adapter,這個adapter會包含了headerview和footerview以及我們傳進來的原來的adapter。這是在18版本以後做的一個處理。雖然有了處理,但是建議大家還是按照上面說的呼叫順序來使用addHeaderView,addFooterView和setAdapter吧。