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

posted @ 2015-12-06 12:29  lx_sunwei  阅读(3986)  评论(0编辑  收藏  举报