RecyclerView系列(四)添加头部和底部封装

使用ListView的都知道ListView中有addHeaderView()和addFooterView()中有添加头部和底部的方法,而RecyclerView却没有,当然也可以自己定义,从网上找的代码在Adapter中添加,太过繁琐。经过分析ListView的实现方法,RecyclerView比着葫芦画瓢同样也能实现,使用更加简单。下面先分析一下ListView的实现。

一、ListView源码分析

先看一下ListView的addHeaderView()方法 源码:

  /**
 * Add a fixed view to appear at the top of the list. If this method is
 * called more than once, the views will appear in the order they were
 * added. Views added using this call can take focus if they want.
 * 

* Note: When first introduced, this method could only be called before * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with * {@link android.os.Build.VERSION_CODES#KITKAT}, this method may be * called at any time. If the ListView's adapter does not extend * {@link HeaderViewListAdapter}, it will be wrapped with a supporting * instance of {@link WrapperListAdapter}. * * @param v The view to add. * @param data Data to associate with this view * @param isSelectable whether the item is selectable */ 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); mAreAllItemsSelectable &= isSelectable; // 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(); } } }

我们可以看到这句代码:

     if (!(mAdapter instanceof HeaderViewListAdapter)) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
        }

不错多了HeaderViewListAdapter类,我们点进去看一下具体实现:

public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {

private final ListAdapter mAdapter;

// These two ArrayList are assumed to NOT be null.
// They are indeed created when declared in ListView and then shared.
ArrayList mHeaderViewInfos;
ArrayList mFooterViewInfos;

// Used as a placeholder in case the provided info views are indeed null.
// Currently only used by some CTS tests, which may be removed.
static final ArrayList EMPTY_INFO_LIST =
    new ArrayList();

public HeaderViewListAdapter(ArrayList headerViewInfos,
                             ArrayList footerViewInfos,
                             ListAdapter adapter) {
   ......省略      
   mAdapter = adapter;
     ......省略
 }

public int getHeadersCount() {
    return mHeaderViewInfos.size();
}

public int getFootersCount() {
    return mFooterViewInfos.size();
}

public boolean isEmpty() {
    return mAdapter == null || mAdapter.isEmpty();
}

private boolean areAllListInfosSelectable(ArrayList infos) {
    if (infos != null) {
        for (ListView.FixedViewInfo info : infos) {
            if (!info.isSelectable) {
                return false;
            }
        }
    }
    return true;
}

public boolean removeHeader(View v) {
    for (int i = 0; i < mHeaderViewInfos.size(); i++) {
        ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
        if (info.view == v) {
            mHeaderViewInfos.remove(i);

            mAreAllFixedViewsSelectable =
                    areAllListInfosSelectable(mHeaderViewInfos)
                    && areAllListInfosSelectable(mFooterViewInfos);

            return true;
        }
    }

    return false;
}

public boolean removeFooter(View v) {
    for (int i = 0; i < mFooterViewInfos.size(); i++) {
        ListView.FixedViewInfo info = mFooterViewInfos.get(i);
        if (info.view == v) {
            mFooterViewInfos.remove(i);

            mAreAllFixedViewsSelectable =
                    areAllListInfosSelectable(mHeaderViewInfos)
                    && areAllListInfosSelectable(mFooterViewInfos);

            return true;
        }
    }

    return false;
}

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

public boolean isEnabled(int position) {
    // Header (negative positions will throw an IndexOutOfBoundsException)
    int numHeaders = getHeadersCount();
    if (position < numHeaders) {
        return mHeaderViewInfos.get(position).isSelectable;
    }

    // Adapter
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getCount();
        if (adjPosition < adapterCount) {
            return mAdapter.isEnabled(adjPosition);
        }
    }

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

public Object getItem(int position) {
    // Header (negative positions will throw an IndexOutOfBoundsException)
    int numHeaders = getHeadersCount();
    if (position < numHeaders) {
        return mHeaderViewInfos.get(position).data;
    }

    // Adapter
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getItem(adjPosition);
        }
    }

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




    // Adapter
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getView(adjPosition, convertView, parent);
        }
    }

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

    return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
public void registerDataSetObserver(DataSetObserver observer) {
    if (mAdapter != null) {
        mAdapter.registerDataSetObserver(observer);
    }
   }

可以看出ListView也不是直接能添加头部和底部,系统封装好了一个包裹类,做了一系列处理,我们才能使用。所以我们也可以仿照它,自己动手封装一个包裹类。

二、自定义RecyclerView包裹类

2.创建可以添加头部和底部的WrapRecyclerAdapter
2.1 创建存放底部和头部View的集合 ,构造创建
private SparseArray mHeaderViews;
private SparseArray mFooterViews;
// 列表的Adapter
private RecyclerView.Adapter mAdapter;
// 基本的头部类型开始位置  用于viewType
private static int BASE_ITEM_TYPE_HEADER = 20000000;
// 基本的底部类型开始位置  用于viewType
private static int BASE_ITEM_TYPE_FOOTER = 30000000;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
    this.mAdapter = adapter;
    mHeaderViews = new SparseArray<>();
    mFooterViews = new SparseArray<>();
}
2.2创建添加和删除头部底部的方法
  // 添加头部
public void addHeaderView(View view) {
    int position = mHeaderViews.indexOfValue(view);
    if (position < 0) {
        mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
    }
    notifyDataSetChanged();
}

// 添加底部
public void addFooterView(View view) {
    int position = mFooterViews.indexOfValue(view);
    if (position < 0) {
        mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
    }
    notifyDataSetChanged();
}

// 移除头部
public void removeHeaderView(View view) {
    int index = mHeaderViews.indexOfValue(view);
    if (index < 0) return;
    mHeaderViews.removeAt(index);
    notifyDataSetChanged();
}

// 移除底部
public void removeFooterView(View view) {
    int index = mFooterViews.indexOfValue(view);
    if (index < 0) return;
    mFooterViews.removeAt(index);
    notifyDataSetChanged();
}
2.3onCreateViewHolder方法
   // viewType 可能就是 SparseArray 的key
    if (isHeaderViewType(viewType)) {
        View headerView = mHeaderViews.get(viewType);
        return createHeaderFooterViewHolder(headerView);
    }

    if (isFooterViewType(viewType)) {
        View footerView = mFooterViews.get(viewType);
        return createHeaderFooterViewHolder(footerView);
    }
    return mAdapter.onCreateViewHolder(parent, viewType);

其中判断是否是头部和底部的方法

/**
 * 是不是底部类型
 */
private boolean isFooterViewType(int viewType) {
    int position = mFooterViews.indexOfKey(viewType);
    return position >= 0;
}
/**
 * 是不是头部类型
 */
private boolean isHeaderViewType(int viewType) {
    int position = mHeaderViews.indexOfKey(viewType);
    return position >= 0;
}

创建头部或者底部的ViewHolder

/**
 * 创建头部或者底部的ViewHolder
 */
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
    return new RecyclerView.ViewHolder(view) {
    };
}
2.4onBindViewHolder方法
   if (isHeaderPosition(position) || isFooterPosition(position)) {
        return;
    }
    // 计算一下位置
    final int adapterPosition = position - mHeaderViews.size();
    mAdapter.onBindViewHolder(holder, adapterPosition);

    // 设置点击和长按事件
    if (mItemClickListener != null) {
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mItemClickListener.onItemClick(adapterPosition);
            }
        });
    }
    if (mLongClickListener != null) {
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return mLongClickListener.onLongClick(adapterPosition);
            }
        });
    }
2.5getItemViewType方法

在onCreateViewHolder方法之前就会调用该方法

   if (isHeaderPosition(position)) {
         // 直接返回position位置的key
        return mHeaderViews.keyAt(position);
    }
    if (isFooterPosition(position)) {
        // 直接返回position位置的key
        position = position - mHeaderViews.size() - mAdapter.getItemCount();
        return mFooterViews.keyAt(position);
    }
    // 返回列表Adapter的getItemViewType
    position = position - mHeaderViews.size();
    return mAdapter.getItemViewType(position);
2.6 getItemCount方法
 @Override
  public int getItemCount() {
    // 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数
    return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
  }

三、创建可以添加头部和底部的RecyclerView

 public class WrapRecyclerView extends RecyclerView {
// 包裹了一层的头部底部Adapter
private WrapRecyclerAdapter mWrapRecyclerAdapter;
// 这个是列表数据的Adapter
private RecyclerView.Adapter mAdapter;

public WrapRecyclerView(Context context) {
    super(context);
}

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public void setAdapter(Adapter adapter) {
    // 为了防止多次设置Adapter
    if (mAdapter != null) {
        mAdapter.unregisterAdapterDataObserver(mDataObserver);
        mAdapter = null;
    }

    this.mAdapter = adapter;

    if (adapter instanceof WrapRecyclerAdapter) {
        mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
    } else {
        mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
    }

    super.setAdapter(mWrapRecyclerAdapter);

    // 注册一个观察者
    mAdapter.registerAdapterDataObserver(mDataObserver);

    // 加载数据页面
    if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
        mLoadingView.setVisibility(View.GONE);
    }

    if (mItemClickListener != null) {
        mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
    }

    if (mLongClickListener != null) {
        mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
    }
}

// 添加头部
public void addHeaderView(View view) {
    // 如果没有Adapter那么就不添加,也可以选择抛异常提示
    // 让他必须先设置Adapter然后才能添加,这里是仿照ListView的处理方式
    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.addHeaderView(view);
    }
}

// 添加底部
public void addFooterView(View view) {
    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.addFooterView(view);
    }
}

// 移除头部
public void removeHeaderView(View view) {
    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.removeHeaderView(view);
    }
}

// 移除底部
public void removeFooterView(View view) {
    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.removeFooterView(view);
    }
}

private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
    @Override
    public void onChanged() {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyDataSetChanged();

        dataChanged();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
        dataChanged();
    }

    @Override
    public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
        dataChanged();
    }

    @Override
    public void onItemRangeChanged(int positionStart, int itemCount) {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyItemChanged(positionStart);
        dataChanged();
    }

    @Override
    public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
        dataChanged();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        if (mAdapter == null) return;
        // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted没效果
        if (mWrapRecyclerAdapter != mAdapter)
            mWrapRecyclerAdapter.notifyItemInserted(positionStart);
        dataChanged();
    }
};

/**
 * Adapter数据改变的方法
 */
private void dataChanged() {
    if (mAdapter.getItemCount() == 0) {
        // 没有数据
        if (mEmptyView != null) {
            mEmptyView.setVisibility(VISIBLE);
        } else {
            mEmptyView.setVisibility(GONE);
        }
    }
}

/*
 * 给条目设置点击和长按事件
 */
public OnItemClickListener mItemClickListener;
public com.hc.recycleranalysis.adapter.OnLongClickListener mLongClickListener;

public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    this.mItemClickListener = itemClickListener;

    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
    }
}

public void setOnLongClickListener(com.hc.recycleranalysis.adapter.OnLongClickListener longClickListener) {
    this.mLongClickListener = longClickListener;

    if (mWrapRecyclerAdapter != null) {
        mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
    }
}
}

你可能感兴趣的:(RecyclerView系列(四)添加头部和底部封装)