RecycleView 滑動到底部,加載更多


  android.support.v7 包提供了一個新的組件:RecycleView,用以提供一個靈活的列表試圖、顯示大型數據集,它支持局部刷新、顯示動畫等功能,可以用來取代ListView與GridView.

  然而在使用的過程中卻遇到一些問題,基本現在手機頁面都會有滑動到底部加載更多的功能,而RecycleView 並沒有提供像ListView的addFooter等方法,所以實現起來還是有點麻煩。經過一段時間的摸索,終於找到實現RecycleView加載更多的方法。

  本文主要通過RecycleView 的 ItemViewType 來實現RecycleView滑動到底部加載更多功能,支持 列表、網格、瀑布流等布局。

 

  根據ItemViewType創建兩種布局,一個用於顯示正常內容的布局,一個用於加載布局,然后監聽RecycleView的滾動事件,當滑動到底部時添加加載布局,對於列表布局來說很簡單,不再熬述,然而對於網格布局與瀑布流布局來說,要解決"加載布局只占用一列"的問題,

  1.對於網格布局,GridLayoutManager 提供了一個 setSpanSizeLookup() 的方法,用來設置每個條目可以占用的列數,默認為1.

  2.對於瀑布流,StaggeredGridLayoutManager 提供了一個 StaggeredGridLayoutManager.LayoutParams 內部靜態類,此類中有一個setFullSpan() 方法,用來設置條目跨越全列。

  

  3.基類的實現方式如下:子類只需要重寫以下方法:

    onCreateNormalViewHolder(ViewGroup parent);

    onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position);

  1 /**
  2  * Created by sunwei on 2015/12/4.
  3  * Email: lx_sunwei@163.com.
  4  * Description: recycleView 滑動到底部加載更多
  5  */
  6 public abstract class BaseLoadingAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
  7 
  8     private static final String TAG = "BaseLoadingAdapter";
  9 
 10     //是否正在加載
 11     public boolean mIsLoading = false;
 12     //正常條目
 13     private static final int TYPE_NORMAL_ITEM = 0;
 14     //加載條目
 15     private static final int TYPE_LOADING_ITEM = 1;
 16     //加載viewHolder
 17     private LoadingViewHolder mLoadingViewHolder;
 18     //瀑布流
 19     private StaggeredGridLayoutManager mStaggeredGridLayoutManager;
 20     //數據集
 21     private CircularArray<T> mTs;
 22     //首次進入
 23     private boolean mFirstEnter = true;
 24     private RecyclerView mRecyclerView;
 25 
 26     public BaseLoadingAdapter(RecyclerView recyclerView, CircularArray<T> ts) {
 27 
 28         mTs = ts;
 29 
 30         mRecyclerView = recyclerView;
 31 
 32         setSpanCount(recyclerView);
 33 
 34         //notifyLoading();
 35     }
 36 
 37     private OnLoadingListener mOnLoadingListener;
 38 
 39     /**
 40      * 加載更多接口
 41      */
 42     public interface OnLoadingListener {
 43         void loading();
 44     }
 45 
 46     /**
 47      * 設置監聽接口
 48      *
 49      * @param onLoadingListener onLoadingListener
 50      */
 51     public void setOnLoadingListener(OnLoadingListener onLoadingListener) {
 52         setScrollListener(mRecyclerView);
 53         mOnLoadingListener = onLoadingListener;
 54     }
 55 
 56     /**
 57      * 加載完成
 58      */
 59     public void setLoadingComplete() {
 60         if (mTs.size() > 0 && mTs.getLast() == null) {
 61             mIsLoading = false;
 62             mTs.removeFromEnd(1);
 63             notifyItemRemoved(mTs.size() - 1);
 64         }
 65     }
 66 
 67     /**
 68      * 沒有更多數據
 69      */
 70     public void setLoadingNoMore() {
 71         mIsLoading = false;
 72         if (mLoadingViewHolder != null) {
 73             mLoadingViewHolder.progressBar.setVisibility(View.GONE);
 74             mLoadingViewHolder.tvLoading.setText("已加載完!");
 75         }
 76     }
 77 
 78     /**
 79      * 加載失敗
 80      */
 81     public void setLoadingError() {
 82         if (mLoadingViewHolder != null) {
 83             mIsLoading = false;
 84             mLoadingViewHolder.progressBar.setVisibility(View.GONE);
 85             mLoadingViewHolder.tvLoading.setText("加載失敗,點擊重新加載!");
 86 
 87             mLoadingViewHolder.tvLoading.setOnClickListener(new View.OnClickListener() {
 88                 @Override
 89                 public void onClick(View v) {
 90                     if (mOnLoadingListener != null) {
 91                         mIsLoading = true;
 92                         mLoadingViewHolder.progressBar.setVisibility(View.VISIBLE);
 93                         mLoadingViewHolder.tvLoading.setText("正在加載...");
 94 
 95                         mOnLoadingListener.loading();
 96                     }
 97                 }
 98             });
 99         }
100     }
101 
102     /**
103      * @return Whether it is possible for the child view of this layout to
104      * scroll up. Override this if the child view is a custom view.
105      */
106     private boolean canScrollDown(RecyclerView recyclerView) {
107         return ViewCompat.canScrollVertically(recyclerView, 1);
108     }
109 
110     /**
111      * 設置加載item占據一行
112      *
113      * @param recyclerView recycleView
114      */
115     private void setSpanCount(RecyclerView recyclerView) {
116         RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
117 
118         if (layoutManager == null) {
119             Log.e(TAG, "LayoutManager 為空,請先設置 recycleView.setLayoutManager(...)");
120         }
121 
122         //網格布局
123         if (layoutManager instanceof GridLayoutManager) {
124             final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
125             gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
126                 @Override
127                 public int getSpanSize(int position) {
128                     int type = getItemViewType(position);
129                     if (type == TYPE_NORMAL_ITEM) {
130                         return 1;
131                     } else {
132                         return gridLayoutManager.getSpanCount();
133                     }
134                 }
135             });
136         }
137 
138         //瀑布流布局
139         if (layoutManager instanceof StaggeredGridLayoutManager) {
140             mStaggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
141         }
142     }
143 
144     /**
145      * 顯示加載
146      */
147     private void notifyLoading() {
148         if (mTs.size() != 0 && mTs.getLast() != null) {
149             mTs.addLast(null);
150             notifyItemInserted(mTs.size() - 1);
151         }
152     }
153 
154     /**
155      * 監聽滾動事件
156      *
157      * @param recyclerView recycleView
158      */
159     private void setScrollListener(RecyclerView recyclerView) {
160         if(recyclerView == null) {
161             Log.e(TAG, "recycleView 為空");
162             return;
163         }
164 
165         recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
166             @Override
167             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
168                 super.onScrollStateChanged(recyclerView, newState);
169             }
170 
171             @Override
172             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
173                 super.onScrolled(recyclerView, dx, dy);
174 
175                 if (!canScrollDown(recyclerView)) {
176 
177                     //首次進入不加載
178                     if (!mIsLoading && !mFirstEnter) {
179 
180                         notifyLoading();
181 
182                         mIsLoading = true;
183 
184                         if (mLoadingViewHolder != null) {
185                             mLoadingViewHolder.progressBar.setVisibility(View.VISIBLE);
186                             mLoadingViewHolder.tvLoading.setText("正在加載...");
187                         }
188 
189                         if (mOnLoadingListener != null) {
190                             mOnLoadingListener.loading();
191                         }
192                     }
193                 }
194 
195                 if (mFirstEnter) {
196                     mFirstEnter = false;
197                 }
198             }
199         });
200     }
201 
202     /**
203      * 創建viewHolder
204      *
205      * @param parent viewGroup
206      * @return viewHolder
207      */
208     public abstract RecyclerView.ViewHolder onCreateNormalViewHolder(ViewGroup parent);
209 
210     /**
211      * 綁定viewHolder
212      *
213      * @param holder   viewHolder
214      * @param position position
215      */
216     public abstract void onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position);
217 
218     /**
219      * 加載布局
220      */
221     private class LoadingViewHolder extends RecyclerView.ViewHolder {
222         public ProgressBar progressBar;
223         public TextView tvLoading;
224         public LinearLayout llyLoading;
225 
226         public LoadingViewHolder(View view) {
227             super(view);
228 
229             progressBar = (ProgressBar) view.findViewById(R.id.progress_loading);
230             tvLoading = (TextView) view.findViewById(R.id.tv_loading);
231             llyLoading = (LinearLayout) view.findViewById(R.id.lly_loading);
232         }
233     }
234 
235     @Override
236     public int getItemViewType(int position) {
237         T t = mTs.get(position);
238         if (t == null) {
239             return TYPE_LOADING_ITEM;
240         } else {
241             return TYPE_NORMAL_ITEM;
242         }
243     }
244 
245     @Override
246     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
247         if (viewType == TYPE_NORMAL_ITEM) {
248             return onCreateNormalViewHolder(parent);
249         } else {
250             View view = LayoutInflater.from(parent.getContext()).inflate(
251                     R.layout.loading_layout, parent, false);
252             mLoadingViewHolder = new LoadingViewHolder(view);
253             return mLoadingViewHolder;
254         }
255     }
256 
257     @Override
258     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
259         int type = getItemViewType(position);
260         if (type == TYPE_NORMAL_ITEM) {
261             onBindNormalViewHolder(holder, position);
262         } else {
263 
264             if (mStaggeredGridLayoutManager != null) {
265                 StaggeredGridLayoutManager.LayoutParams layoutParams =
266                         new StaggeredGridLayoutManager.LayoutParams(
267                                 ViewGroup.LayoutParams.MATCH_PARENT,
268                                 ViewGroup.LayoutParams.WRAP_CONTENT);
269                 layoutParams.setFullSpan(true);
270 
271                 mLoadingViewHolder.llyLoading.setLayoutParams(layoutParams);
272             }
273         }
274     }
275 
276     @Override
277     public int getItemCount() {
278         return mTs.size();
279     }
280 }
Base Loading Adapter

 

      4.一個簡單的列子

 1 /**
 2  * Created by sunwei on 2015/12/4.
 3  * Email: lx_sunwei@163.com.
 4  * Description: 滑動到底部加載更多
 5  */
 6 public class DesignLoaderMoreAdapter extends BaseLoadingAdapter<DesignItem> {
 7 
 8     private CircularArray<DesignItem> mDesignItems;
 9 
10     public DesignLoaderMoreAdapter(RecyclerView recyclerView, CircularArray<DesignItem> datas) {
11         super(recyclerView, datas);
12 
13         mDesignItems = datas;
14     }
15 
16     //正常條目
17     public  class DesignViewHolder extends RecyclerView.ViewHolder {
18         public TextView  textView;
19         public CardView cardView;
20         public DesignViewHolder(View view) {
21             super(view);
22             textView = (TextView) view.findViewById(R.id.tv_design);
23             cardView = (CardView) view.findViewById(R.id.cardView_designer);
24 
25         }
26     }
27 
28     @Override
29     public RecyclerView.ViewHolder onCreateNormalViewHolder(ViewGroup parent) {
30         View view = LayoutInflater.from(parent.getContext()).inflate(
31                 R.layout.list_item_design, parent, false);
32         return new DesignViewHolder(view);
33     }
34 
35     @Override
36     public void onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position) {
37         DesignViewHolder viewHolder = (DesignViewHolder)holder;
38         DesignItem designItem = mDesignItems.get(position);
39         if (position == 10) {
40             //設置瀑布流的條目大小
41             LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(260, 360);
42             lp.setMargins(10, 40, 10, 80);
43             viewHolder.cardView.setLayoutParams(lp);
44         }
45 
46         viewHolder.textView.setText(designItem.name);
47     }
48 }
Sample

 

      

 

項目地址:https://github.com/lxsunwei/MaterialDesign


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM