天天看點

ListView源碼分析之添加HeaderView(或FooterView)實作原理

首先,RecyclerView 功能雖然強大但是沒有像ListView的addHaderView()或者addFooterView之類的方法,但是我們實際開發中可能需要這麼去做,必須我們自己去實作,為了給RecyclerView添加header 或者footer 我們今天來分析ListView是如何實作此功能的。

一、我們直接來看ListView.addHeaderView()方法,重點就是我們的adapter.

public void addHeaderView(View v, Object data, boolean isSelectable) {
		//可以看到ListView把我們的HeaderView封裝成了對象FixedViewInfo 存在了集合當中
		//private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
		//private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();
		//說明可以添加多個headerView 和 footerView
		//
		final FixedViewInfo info = new FixedViewInfo();
		info.view = v;
		info.data = data;
		info.isSelectable = isSelectable;
		mHeaderViewInfos.add(info);
		mAreAllItemsSelectable &= isSelectable;

		// Wrap the adapter if it wasn't already wrapped.
		if (mAdapter != null) {
		   // 如果設定的adapter不是HeaderViewListAdapter 就建立一個HeaderViewListAdapter 并把mAdapter傳進去
		   // 這裡就是說如果 添加有headerView或者footerView 就是用HeaderViewListAdapter來裝飾我們的adapter
		   // 裝飾模式
		    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();
		    }
		}
	    }
           

二、繼續來看ListView.setAdapter()方法

@Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();
	 這裡和 addHeaderView方法一樣裝飾了我們的adapter
        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

       ........................

        requestLayout();
    }
           

三、上面我們看到了 HeaderViewListAdapter來裝飾我們自己的adapter 我們這裡就來看一下  HeaderViewListAdapter

public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {

    private final ListAdapter mAdapter;
           

再看WraperListAdapter:

public interface WrapperListAdapter extends ListAdapter {
    /**
     * Returns the adapter wrapped by this list adapter.
     *
     * @return The {@link android.widget.ListAdapter} wrapped by this adapter.
     */
    public ListAdapter getWrappedAdapter();
}
           

可以看到  HeaderViewListAdapter 和我們自己的傳進來的Adapter 都實作了ListAdapter接口,

ListView源碼分析之添加HeaderView(或FooterView)實作原理

這和我們普通的adapter沒有什麼大的差別 都擁有常用的方法 比如:getView()、getItem()、getCount()、getItemId()等

繼續看getView()方法:

//重點看這裡:
    public View getView(int position, View convertView, ViewGroup parent) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).view;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
	    // 代理(做一個切面的程式設計):根據不同的position傳回不同的view
            if (adjPosition < adapterCount) {
                return mAdapter.getView(adjPosition, convertView, parent);
            }
        }

        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).view;
    }
           

看到這裡我想大家都明白了,getCount(),getItem()也是同樣如此:

public int getCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }
           

getItemId()方法:

public long getItemId(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemId(adjPosition);
            }
        }
        return -1;
    }
           

看到這裡我們了解了 ListView添加HeaderView 和 FooterView的實作原理,可以為RecyclerView添加簡單的HeaderView 或者FooterView了。每天學一點,加油!