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 }
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 }