RecyclerView--添加頭部和底部

来源:https://www.cnblogs.com/ganchuanpu/archive/2018/01/12/8275368.html
-Advertisement-
Play Games

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 }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 寫在前面 近兩年分散式資料庫技術加速發展,而由於金融行業技術生態的限制,周圍很多同學對其並沒有深入的瞭解,所以進行高性能、高可靠系統設計時往往缺少這一利器。Ivan希望以系列文章的方式與大家交流探討,加深我們對分散式資料庫的認識。本文是該系列文章的第一篇,主要探討事務管理中的隔離性,釐清相關概念和關 ...
  • Spirng Boot構建 1、輸入命令 mvn archetype:generate -Dinteractivemodel=false -DgroupId=com.immoc -DartifactId=first-app-by-maven -Dversion=1.0.0-SNAPSHOT。 2、通 ...
  • 本文簡要介紹了Android設備上Unity應用遠程profiling的方法。 ...
  • 在目錄上面右擊,有 Reformat Code Ctrl + Alt + L 參考 "如何在IntelliJ IDEA或Android Studio中一次性格式化所有代碼?" ...
  • 這篇博客要講的是自定義switchButton,不過沒有設置動畫效果。 我用GradientDrawable來繪製switchButton,我們先看看最終的效果: 點擊前: 點擊後 接下來我們看看如何實現 首先:該類繼承RelativeLayout 該類需要用到的成員: 構造方法: 設置點擊事件 以 ...
  • iOS 10 以後記得在plist裡面添加獲取拍照和相冊許可權,iOS 10 以後記得在plist裡面添加獲取拍照和相冊許可權,iOS 10 以後記得在plist裡面添加獲取拍照和相冊許可權,iOS 10 以後記得在plist裡面添加獲取拍照和相冊許可權,iOS 10 以後記得在plist裡面添加獲取拍照和 ...
  • 使用android studio開發程式時,有時會出現 waiting for debugger 卡住界面,軟體無法正常debug運行的情況,很多網友分享了一些解決辦法,比如: 1 打開cmd進入命令行,進入adb所在目錄先後執行adb kill-server,adb start-server。 2 ...
  • 由於業務需要,需要將開發的App從一個賬號(A賬號)轉移到另一個賬號(B賬號),這裡簡單介紹一下轉讓流程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...