天天看点

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了。每天学一点,加油!