RecyclerView 添加頭部和尾部布局,recyclerview尾部
RecyclerView 出來有很長一段時間了,相信大家對它已經很熟悉了,使用過它的朋友可能都會發現一點,就是 RecyclerView 不能添加 headerView 和 footView,這就讓我們有點蛋疼了,也許你會說,沒事啊,我們可以重寫getItemViewType(int position)這個方法,讓他實現多個布局,具體實現如下:
@Override public int getItemViewType(int position) { if (position == 0) { return HEAD_VIEW; }else{ return BODY_VIEW ; } }
下面的操作就不多講了,這種方法相信大家在使用 ListView 的時候就非常熟練; 那既然可以通過以上方法實現,為什麼還要寫一個可以添加 HeaderView 和 FootView 的 RecyclerView ?其實這個問題不用回答,因為 ListView 也可以通過以上方法實現,但它還是有 addHeaderView和 addFootView 的方法,不過話說回來,通過 addHeaderView 實現的添加頭部布局確實有不可代替的功能,比如,ListView 頭部圖片下拉放大動畫;在調用 adapter.notifyDataSetChanged()頭部資料是不會重新重新整理的等等。
好了,接下來才是本文的主題內容,如何? RecyclerView 添加 HeaderView 和 FootView?
無從下手?沒關係!ListView 不是已經幫我們實現了這樣的功能嗎,我們可以先看看 ListView 是如何?的,
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(); } } }
以上方法核心代碼就是
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
其他可以忽略不看;這裡其實用到了一種設計模式的概念,好像是叫什麼裝飾模式吧,意思就是把原有的 Adapter 通過HeaderViewListAdapter這個封裝類重新封裝一次,把改增的地方增一增,該減的地方減一點;所以我們要關注的是HeaderViewListAdapter類是如何?的,然後再依葫蘆畫瓢畫一個封裝 RecyclerView.Adapter的封裝類!
解密HeaderViewListAdapter
這個封裝類代碼本來就不長,相信大家都能夠理解的,這裡就挑點重點講一下
1、對Adapter 的 count 進行了重新計算,這個不用多解釋吧!
public int getCount() { if (mAdapter != null) { return getFootersCount() + getHeadersCount() + mAdapter.getCount(); } else { return getFootersCount() + getHeadersCount(); } }
2、就是getView方法,這個方法是重點,如果當前 position 的位置比 HeaderView 的數量小,那麼返回的就是 HeaderView 的 對應的 View,否則再判斷 原 Adapter 的 count 與當前 position 的差值來比較,是調用原 Adapter 的 getView 方法,還是擷取 footView 的 view;說白了這個方法的目的就是添加了頭部和尾部 View。
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(); if (adjPosition < adapterCount) { return mAdapter.getView(adjPosition, convertView, parent); } } // Footer (off-limits positions will throw an IndexOutOfBoundsException) return mFooterViewInfos.get(adjPosition - adapterCount).view; }
對於這個封裝類的其他方法只要注意一下就 沒問題了,那麼接下來,就是我們模仿的時刻了!
代碼非常簡單,這裡就直接上了
1、封裝類 RecyclerWrapAdapter
package moon.myapplication;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* Created by moon.zhong on 2015/7/20.
* time : 14:10
*/
public class RecyclerWrapAdapter extends RecyclerView.Adapter implements WrapperAdapter {
private RecyclerView.Adapter mAdapter;private ArrayList<View> mHeaderViews;private ArrayList<View> mFootViews;static final ArrayList<View> EMPTY_INFO_LIST = new ArrayList<View>();private int mCurrentPosition;public RecyclerWrapAdapter(ArrayList<View> mHeaderViews, ArrayList<View> mFootViews, RecyclerView.Adapter mAdapter) { this.mAdapter = mAdapter; if (mHeaderViews == null) { this.mHeaderViews = EMPTY_INFO_LIST; } else { this.mHeaderViews = mHeaderViews; } if (mHeaderViews == null) { this.mFootViews = EMPTY_INFO_LIST; } else { this.mFootViews = mFootViews; }}public int getHeadersCount() { return mHeaderViews.size();}public int getFootersCount() { return mFootViews.size();}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == RecyclerView.INVALID_TYPE) { return new HeaderViewHolder(mHeaderViews.get(0)); } else if (viewType == RecyclerView.INVALID_TYPE - 1) { return new HeaderViewHolder(mFootViews.get(0)); } return mAdapter.onCreateViewHolder(parent, viewType);}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int numHeaders = getHeadersCount(); if (position < numHeaders) { return; } int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { mAdapter.onBindViewHolder(holder, adjPosition); return; } }}@Overridepublic int getItemCount() { if (mAdapter != null) { return getHeadersCount() + getFootersCount() + mAdapter.getItemCount(); } else { return getHeadersCount() + getFootersCount(); }}@Overridepublic int getItemViewType(int position) { mCurrentPosition = position; int numHeaders = getHeadersCount(); if (position < numHeaders) { return RecyclerView.INVALID_TYPE; } int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { return mAdapter.getItemViewType(adjPosition); } } return RecyclerView.INVALID_TYPE - 1;}@Overridepublic long getItemId(int position) { int numHeaders = getHeadersCount(); if (mAdapter != null && position >= numHeaders) { int adjPosition = position - numHeaders; int adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { return mAdapter.getItemId(adjPosition); } } return -1;}@Overridepublic RecyclerView.Adapter getWrappedAdapter() { return mAdapter;}private static class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); }}
}
2、為 RecyclerView 添加 AddHeaderView 和 addFootView 方法,重寫 RecyclerView
package moon.myapplication;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.util.AttributeSet;import android.view.View;import java.util.ArrayList;/** * Created by moon.zhong on 2015/7/20. * time : 15:14 */public class WrapRecyclerView extends RecyclerView { private ArrayList<View> mHeaderViews = new ArrayList<>() ; private ArrayList<View> mFootViews = new ArrayList<>() ; private Adapter mAdapter ; public WrapRecyclerView(Context context) { super(context); } public WrapRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public WrapRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void addHeaderView(View view){ mHeaderViews.clear(); mHeaderViews.add(view); if (mAdapter != null){ if (!(mAdapter instanceof RecyclerWrapAdapter)){ mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;// mAdapter.notifyDataSetChanged(); } } } public void addFootView(View view){ mFootViews.clear(); mFootViews.add(view); if (mAdapter != null){ if (!(mAdapter instanceof RecyclerWrapAdapter)){ mAdapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;// mAdapter.notifyDataSetChanged(); } } } @Override public void setAdapter(Adapter adapter) { if (mHeaderViews.isEmpty()&&mFootViews.isEmpty()){ super.setAdapter(adapter); }else { adapter = new RecyclerWrapAdapter(mHeaderViews,mFootViews,adapter) ; super.setAdapter(adapter); } mAdapter = adapter ; }}
到這裡就實現了 RecyclerView 添加頭部和尾部布局 ,用法很簡單,其他步驟都不變,只需把 RecyclerView 換成WrapRecyclerView即可。
看看
這篇文章沒有什麼創新的技術點,但卻給廣大程式員一個很好的思路:那就是模仿,最好是能自己動手,也許你看一遍就能理解,但是真正動手實踐的時候你會發現很多問題!
源碼 Download
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。