1.先構建WrapRecyclerAdapter 2.構建WrapRecyclerView 我們最好還是模仿ListView的結構搞就搞到西,自定義一個WrapRecyclerView,可以添加刪除頭部和底部View,這個就比較簡單 3.使用: ...
1.先構建WrapRecyclerAdapter
1 /** 2 * Description: 可以添加頭部和底部的Adapter 3 */ 4 public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 5 private final static String TAG = "WrapRecyclerAdapter"; 6 7 /** 8 * SparseArrays map integers to Objects. Unlike a normal array of Objects, 9 * there can be gaps in the indices. It is intended to be more memory efficient 10 * than using a HashMap to map Integers to Objects, both because it avoids 11 * auto-boxing keys and its data structure doesn't rely on an extra entry object 12 * for each mapping. 13 * 14 * SparseArray是一個<int , Object>的HashMap 比HashMap更高效 15 */ 16 private SparseArray<View> mHeaderViews; 17 private SparseArray<View> mFooterViews; 18 19 // 基本的頭部類型開始位置 用於viewType 20 private static int BASE_ITEM_TYPE_HEADER = 10000000; 21 // 基本的底部類型開始位置 用於viewType 22 private static int BASE_ITEM_TYPE_FOOTER = 20000000; 23 24 /** 25 * 數據列表的Adapter 26 */ 27 private RecyclerView.Adapter mAdapter; 28 29 public WrapRecyclerAdapter(RecyclerView.Adapter adapter) { 30 this.mAdapter = adapter; 31 mHeaderViews = new SparseArray<>(); 32 mFooterViews = new SparseArray<>(); 33 } 34 35 @Override 36 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 37 38 // viewType 可能就是 SparseArray 的key 39 if (isHeaderViewType(viewType)) { 40 View headerView = mHeaderViews.get(viewType); 41 return createHeaderFooterViewHolder(headerView); 42 } 43 44 if (isFooterViewType(viewType)) { 45 View footerView = mFooterViews.get(viewType); 46 return createHeaderFooterViewHolder(footerView); 47 } 48 return mAdapter.onCreateViewHolder(parent, viewType); 49 } 50 51 /** 52 * 是不是底部類型 53 */ 54 private boolean isFooterViewType(int viewType) { 55 int position = mFooterViews.indexOfKey(viewType); 56 return position >= 0; 57 } 58 59 /** 60 * 創建頭部或者底部的ViewHolder 61 */ 62 private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) { 63 return new RecyclerView.ViewHolder(view) { 64 65 }; 66 } 67 68 /** 69 * 是不是頭部類型 70 */ 71 private boolean isHeaderViewType(int viewType) { 72 int position = mHeaderViews.indexOfKey(viewType); 73 return position >= 0; 74 } 75 76 @Override 77 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 78 if (isHeaderPosition(position) || isFooterPosition(position)) { 79 return; 80 } 81 82 // 計算一下位置 83 final int adapterPosition = position - mHeaderViews.size(); 84 mAdapter.onBindViewHolder(holder, adapterPosition); 85 86 // 設置點擊和長按事件 87 if (mItemClickListener != null) { 88 holder.itemView.setOnClickListener(new View.OnClickListener() { 89 @Override 90 public void onClick(View v) { 91 mItemClickListener.onItemClick(v, adapterPosition); 92 } 93 }); 94 } 95 if (mLongClickListener != null) { 96 holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 97 @Override 98 public boolean onLongClick(View v) { 99 return mLongClickListener.onLongClick(v, adapterPosition); 100 } 101 }); 102 } 103 } 104 105 @Override 106 public int getItemViewType(int position) { 107 if (isHeaderPosition(position)) { 108 // 直接返回position位置的key 109 return mHeaderViews.keyAt(position); 110 } 111 if (isFooterPosition(position)) { 112 // 直接返回position位置的key 113 position = position - mHeaderViews.size() - mAdapter.getItemCount(); 114 return mFooterViews.keyAt(position); 115 } 116 // 返回列表Adapter的getItemViewType 117 position = position - mHeaderViews.size(); 118 return mAdapter.getItemViewType(position); 119 } 120 121 /** 122 * 是不是底部位置 123 */ 124 private boolean isFooterPosition(int position) { 125 return position >= (mHeaderViews.size() + mAdapter.getItemCount()); 126 } 127 128 /** 129 * 是不是頭部位置 130 */ 131 private boolean isHeaderPosition(int position) { 132 return position < mHeaderViews.size(); 133 } 134 135 @Override 136 public int getItemCount() { 137 // 條數三者相加 = 底部條數 + 頭部條數 + Adapter的條數 138 return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size(); 139 } 140 141 /** 142 * 獲取列表的Adapter 143 */ 144 private RecyclerView.Adapter getAdapter() { 145 return mAdapter; 146 } 147 148 // 添加頭部 149 public void addHeaderView(View view) { 150 int position = mHeaderViews.indexOfValue(view); 151 if (position < 0) { 152 mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view); 153 } 154 notifyDataSetChanged(); 155 } 156 157 // 添加底部 158 public void addFooterView(View view) { 159 int position = mFooterViews.indexOfValue(view); 160 if (position < 0) { 161 mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view); 162 } 163 notifyDataSetChanged(); 164 } 165 166 // 移除頭部 167 public void removeHeaderView(View view) { 168 int index = mHeaderViews.indexOfValue(view); 169 if (index < 0) return; 170 mHeaderViews.removeAt(index); 171 notifyDataSetChanged(); 172 } 173 174 // 移除底部 175 public void removeFooterView(View view) { 176 int index = mFooterViews.indexOfValue(view); 177 if (index < 0) return; 178 mFooterViews.removeAt(index); 179 notifyDataSetChanged(); 180 } 181 182 /** 183 * 解決GridLayoutManager添加頭部和底部不占用一行的問題 184 * 185 * @param recycler 186 * @version 1.0 187 */ 188 public void adjustSpanSize(RecyclerView recycler) { 189 if (recycler.getLayoutManager() instanceof GridLayoutManager) { 190 final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager(); 191 layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 192 @Override 193 public int getSpanSize(int position) { 194 boolean isHeaderOrFooter = 195 isHeaderPosition(position) || isFooterPosition(position); 196 return isHeaderOrFooter ? layoutManager.getSpanCount() : 1; 197 } 198 }); 199 } 200 } 201 202 /*************** 203 * 給條目設置點擊和長按事件 204 *********************/ 205 public OnItemClickListener mItemClickListener; 206 public OnLongClickListener mLongClickListener; 207 208 public void setOnItemClickListener(OnItemClickListener itemClickListener) { 209 this.mItemClickListener = itemClickListener; 210 } 211 212 public void setOnLongClickListener(OnLongClickListener longClickListener) { 213 this.mLongClickListener = longClickListener; 214 } 215 }
2.構建WrapRecyclerView
我們最好還是模仿ListView的結構搞就搞到西,自定義一個WrapRecyclerView,可以添加刪除頭部和底部View,這個就比較簡單
1 /** 2 * Description: 可以添加頭部和底部的RecyclerView 3 */ 4 public class WrapRecyclerView extends RecyclerView { 5 // 包裹了一層的頭部底部Adapter 6 private WrapRecyclerAdapter mWrapRecyclerAdapter; 7 // 這個是列表數據的Adapter 8 private Adapter mAdapter; 9 10 // 增加一些通用功能 11 // 空列表數據應該顯示的空View 12 // 正在載入數據頁面,也就是正在獲取後臺介面頁面 13 private View mEmptyView, mLoadingView; 14 15 public WrapRecyclerView(Context context) { 16 super(context); 17 } 18 19 public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) { 20 super(context, attrs); 21 } 22 23 public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 24 super(context, attrs, defStyle); 25 } 26 27 @Override 28 public void setAdapter(Adapter adapter) { 29 // 為了防止多次設置Adapter 30 if (mAdapter != null) { 31 mAdapter.unregisterAdapterDataObserver(mDataObserver); 32 mAdapter = null; 33 } 34 35 this.mAdapter = adapter; 36 37 if (adapter instanceof WrapRecyclerAdapter) { 38 mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter; 39 } else { 40 mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter); 41 } 42 43 super.setAdapter(mWrapRecyclerAdapter); 44 45 // 註冊一個觀察者 46 mAdapter.registerAdapterDataObserver(mDataObserver); 47 48 // 解決GridLayout添加頭部和底部也要占據一行 49 mWrapRecyclerAdapter.adjustSpanSize(this); 50 51 // 載入數據頁面 52 if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) { 53 mLoadingView.setVisibility(View.GONE); 54 } 55 56 if (mItemClickListener != null) { 57 mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener); 58 } 59 60 if (mLongClickListener != null) { 61 mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener); 62 } 63 } 64 65 // 添加頭部 66 public void addHeaderView(View view) { 67 // 如果沒有Adapter那麼就不添加,也可以選擇拋異常提示 68 // 讓他必須先設置Adapter然後才能添加,這裡是仿照ListView的處理方式 69 if (mWrapRecyclerAdapter != null) { 70 mWrapRecyclerAdapter.addHeaderView(view); 71 } 72 } 73 74 // 添加底部 75 public void addFooterView(View view) { 76 if (mWrapRecyclerAdapter != null) { 77 mWrapRecyclerAdapter.addFooterView(view); 78 } 79 } 80 81 // 移除頭部 82 public void removeHeaderView(View view) { 83 if (mWrapRecyclerAdapter != null) { 84 mWrapRecyclerAdapter.removeHeaderView(view); 85 } 86 } 87 88 // 移除底部 89 public void removeFooterView(View view) { 90 if (mWrapRecyclerAdapter != null) { 91 mWrapRecyclerAdapter.removeFooterView(view); 92 } 93 } 94 95 private AdapterDataObserver mDataObserver = new AdapterDataObserver() { 96 @Override 97 public void onChanged() { 98 if (mAdapter == null) return; 99 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒效果 100 if (mWrapRecyclerAdapter != mAdapter) 101 mWrapRecyclerAdapter.notifyDataSetChanged(); 102 103 dataChanged(); 104 } 105 106 @Override 107 public void onItemRangeRemoved(int positionStart, int itemCount) { 108 if (mAdapter == null) return; 109 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒效果 110 if (mWrapRecyclerAdapter != mAdapter) 111 mWrapRecyclerAdapter.notifyItemRemoved(positionStart); 112 dataChanged(); 113 } 114 115 @Override 116 public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 117 if (mAdapter == null) return; 118 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved沒效果 119 if (mWrapRecyclerAdapter != mAdapter) 120 mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition); 121 dataChanged(); 122 } 123 124 @Override 125 public void onItemRangeChanged(int positionStart, int itemCount) { 126 if (mAdapter == null) return; 127 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒效果 128 if (mWrapRecyclerAdapter != mAdapter) 129 mWrapRecyclerAdapter.notifyItemChanged(positionStart); 130 dataChanged(); 131 } 132 133 @Override 134 public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { 135 if (mAdapter == null) return; 136 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒效果 137 if (mWrapRecyclerAdapter != mAdapter) 138 mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload); 139 dataChanged(); 140 } 141 142 @Override 143 public void onItemRangeInserted(int positionStart, int itemCount) { 144 if (mAdapter == null) return; 145 // 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted沒效果 146 if (mWrapRecyclerAdapter != mAdapter) 147 mWrapRecyclerAdapter.notifyItemInserted(positionStart); 148 dataChanged(); 149 } 150 }; 151 152 /** 153 * 添加一個空列表數據頁面 154 */ 155 public void addEmptyView(View emptyView) { 156 this.mEmptyView = emptyView; 157 } 158 159 /** 160 * 添加一個正在載入數據的頁面 161 */ 162 public void addLoadingView(View loadingView) { 163 this.mLoadingView = loadingView; 164 mLoadingView.setVisibility(View.VISIBLE); 165 } 166 167 /** 168 * Adapter數據改變的方法 169 */ 170 private void dataChanged() { 171 if (mAdapter.getItemCount() == 0) { 172 // 沒有數據 173 if (mEmptyView != null) { 174 mEmptyView.setVisibility(VISIBLE); 175 } 176 } else { 177 // 沒有數據 178 if (mEmptyView != null) { 179 mEmptyView.setVisibility(GONE); 180 } 181 } 182 } 183 184 /*************** 185 * 給條目設置點擊和長按事件 186 *********************/ 187 public com.zzw.framelibray.recyclerview.adapter.OnItemClickListener mItemClickListener; 188 public com.zzw.framelibray.recyclerview.adapter.OnLongClickListener mLongClickListener; 189 190 public void setOnItemClickListener(com.zzw.framelibray.recyclerview.adapter.OnItemClickListener itemClickListener) { 191 this.mItemClickListener = itemClickListener; 192 193 if (mWrapRecyclerAdapter != null) { 194 mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener); 195 } 196 } 197 198 public void setOnLongClickListener(com.zzw.framelibray.recyclerview.adapter.OnLongClickListener longClickListener) { 199 this.mLongClickListener = longClickListener; 200 201 if (mWrapRecyclerAdapter != null) { 202 mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener); 203 } 204 } 205 }
3.使用:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <com.zzw.framelibray.recyclerview.view.WrapRecyclerView 7 android:id="@+id/wrap_recycler_view" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" 10 11 /> 12 13 </LinearLayout>
Activity
1 public class HeaderFooterActivity extends AppCompatActivity implements OnItemClickListener { 2 3 private WrapRecyclerView mRecyclerView; 4 private List<People> mData; 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.activity_recycler_view); 10 mRecyclerView = (WrapRecyclerView) findViewById(R.id.wrap_recycler_view); 11 mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 12 13 mData =new ArrayList<>(); 14 mData.add(new People()); 15 mData.add(new People()); 16 mData.add(new People()); 17 18 PeopleListAdapter listAdapter = new PeopleListAdapter(this, mData); 19 20 // 添加頭部和底部 需要 包裹Adapter,才能添加頭部和底部 21 WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(listAdapter); 22 mRecyclerView.setAdapter(wrapRecyclerAdapter); 23 24 wrapRecyclerAdapter.setOnItemClickListener(this); 25 26 // 添加頭部和底部 27 wrapRecyclerAdapter.addHeaderView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false)); 28 wrapRecyclerAdapter.addFooterView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false)); 29 } 30 31 @Override 32 public void onItemClick(View view, int position) { 33 Toast.makeText(this, "" + mData.get(position).name, Toast.LENGTH_SHORT).show(); 34 } 35 36 class PeopleListAdapter extends CommonRecyclerAdapter<People> { 37 38 public PeopleListAdapter(Context context, List<People> datas) { 39 super(context, datas, R.layout.channel_list_item); 40 } 41 42 @Override 43 public void convert(ViewHolder holder, People item, int position) { 44 holder.setText(R.id.action_btn, item.name+position); 45 } 46 } 47 48 class People{ 49 String name="王偉:"; 50 } 51 }