项目地址:https://github.com/HolenZhou/PullRecyclerView
效果展示
简介
RecyclerView已经出来两年多了,但直到今年4月份,我们的项目里才开始逐渐将原来的ListView替换为RecyclerView,当时项目里使用的是PullToRefresh这个开源里面的XListView,简单实用地实现了ListView的下拉刷新和上拉加载,不会引入其他多余的代码,同时配合使用base-adapter-helper这个项目提供的adapter及其辅助类,使得ListView的使用更加简单,代码更加精简。所以当初替换的时候,我的思路就是需要找到一个跟XListView一样独立可使用的封装了下拉和上拉的RecyclerView控件,并且最好能够保留当前这个好用的adapter,以使替换的工作量降到最低。
当时在github上看了很多但都觉得不太满意,有的是功能太多,需要引入太多无关的代码,有的是效果不满足我的预期。加之当时听了Stay大神的课程,看到他对RecyclerView的简单封装,所以就以Stay的源码为雏形,结合自己项目中实际使用的需要,重新封装了一个PullRecyclerView,Stay关于封装RecyclerView的代码github上也有,想学习的可以去瞅瞅,不但可以看到对于RecyclerView的封装,还可以看到如何封装一些好用的Base类。同时还借鉴了另外一个项目BaseRecyclerViewAdapterHelper,当时这个项目还只有不到1千个star,短短几个月,已经快达到5千个star,而且这个项目一直在精心维护中,现在的代码跟我当初看到的代码比起来进行了很多修改和优化,它的宗旨是提供一个“Powerful and flexible RecyclerAdapter”,所有相关的功能(下拉、上拉、header、footer、空页面等)都是通过它提供的adapter来实现的,没有对RecyclerView本身做任何的封装。其实不用封装RecyclerView确实完全可以达到任何需求,因为RecyclerView的下拉刷新不需要它本身实现,而是通过官方提供的SwipeRefreshLayout来实现的,而上拉、header、footer和空页面这些功能,则只通过adapter的操作就可以完全实现。但对于我来讲,封装也是有好处的,将SwipeRefreshLayout和RecyclerView组合成一个控件可以让我们使用起来更加简洁和方便,另外对于加载更多的效果,个人认为通过RecyclerView的滚动来触发比通过adapter来触发在效果上更加自然。所以最终我没有直接使用BaseRecyclerViewAdapterHelper项目,而是借鉴了一些它里面对于adapter的封装和使用,自己进行了更加简洁精小的封装。
我只加入了最核心和最常用的功能封装:
1.下拉刷新和上拉加载更多(支持普通列表和Grid列表,暂不支持瀑布流);
2.添加Header和Footer;
3.设置列表无数据时需要展示的空页面
4.非常好用的BaseRecyclerAdapter和BaseViewHolder;
5.只支持上拉加载更多的LoadMoreRecyclerView(直接继承自RecyclerView,适用于需要将SwipeRefreshLayout和RecyclerView分开使用的场景)。
从4月份加入我们公司的项目至今,已经历经了三个版本和大量用户的考验,所以想要使用的话是可以直接用于实际项目的。欢迎感兴趣的童鞋们star我的PullRecyclerView,也欢迎你们提出问题和建议!
使用方法
一. 下拉刷新和上拉加载更多
1.初始化,跟RecyclerView的是使用方法一致
mPullRecyclerView = (PullRecyclerView) findViewById(R.id.pull_recycler_view);
// 设置LayoutManager(这里需要使用lib里面提供的XLinearLayoutManager或XGridLayoutManager,默认为XLinearLayoutManager)
mPullRecyclerView.setLayoutManager(new XLinearLayoutManager(this));
// 创建adapter,adapter的使用方法看后面
mAdapter = new CommonListAdapter(this, R.layout.list_item, mDataList);
// 设置adapter
mPullRecyclerView.setAdapter(mAdapter);
// 设置下拉刷新的旋转圆圈的颜色(根据自己的需求设置)
mPullRecyclerView.setColorSchemeResources(R.color.colorAccent);
// 设置ItemDecoration,可选
mPullRecyclerView.addItemDecoration(...);
// 设置当数据全部加载完成时,是否在列表底部展示提示语,可选,默认无提示
mPullRecyclerView.enableLoadDoneTip(true, R.string.load_done_tip);
2.下拉刷新
// 开启下拉刷新,默认即为true,可不用设置
mPullRecyclerView.enablePullRefresh(true);
// 下拉刷新成功后更新UI,结束下拉刷新
mPullRecyclerView.stopRefresh();
3.上拉加载更多
// 开启上拉加载更多,默认为false,当前有更多数据需要加载时需开启
mPullRecyclerView.enableLoadMore(true);
// 上拉加载成功后更新UI,结束上拉加载
mPullRecyclerView.stopLoadMore();
4.上拉、下拉事件监听
mPullRecyclerView.setOnRecyclerRefreshListener(new PullRecyclerView.OnRecyclerRefreshListener() {
@Override
public void onPullRefresh() {
// 下拉刷新事件被触发
}
@Override
public void onLoadMore() {
// 上拉加载更多事件被触发
}
});
5.代码触发列表刷新
// 触发PullRecyclerView的下拉刷新,会展示下拉旋转圆圈
mPullRecyclerView.postRefreshing();
// 直接刷新列表(回调监听事件中的onPullRefresh方法),不展示下拉旋转圆圈
mPullRecyclerView.refreshNoPull();
6.item点击事件监听
// 通过adapter设置item的点击事件
mAdapter.setOnRecyclerItemClickListener(new BaseRecyclerAdapter.OnRecyclerItemClickListener() {
@Override
public void onItemClick(View view, int position) {
}
});
// 通过adapter设置item的长按事件
mAdapter.setOnRecyclerItemLongClickListener(new BaseRecyclerAdapter.OnRecyclerItemLongClickListener() {
@Override
public boolean onItemLongClick(View view, int position) {
return false;
}
});
二. 添加Header和Footer
添加header和footer功能是通过BaseRecyclerAdapter类实现的,所以最终会调用到BaseRecyclerAdapter中的方法,但是为了方便,我在PullRecyclerView中也添加了同样的方法,注意使用PullRecyclerView中的添加Header和Footer的方法时,必须在给PullRecyclerView设置了adapter之后再进行调用。
1.添加Header(两种方法任选其一)
// 添加header,传入header对应的View
mPullRecyclerView.addHeaderView(headView);
// 添加header,传入header对应的布局资源文件
mPullRecyclerView.addHeaderView(R.layout.layout_list_header);
2.添加Footer(添加了Footer,上拉加载的功能就不再可用)
// 添加footer,传入footer对应的View
mPullRecyclerView.addFooterView(footerView);
// 添加footer,传入footer对应的布局资源文件
mPullRecyclerView.addFooterView(R.layout.layout_list_footer);
三. 设置空页面
空页面的实现原理跟添加header和footer是一样的,所以同样注意使用PullRecyclerView中的设置空页面的方法时,必须在给PullRecyclerView设置了adapter之后再进行调用。
1.无数据且无Header时展示空页面(两种方法任选其一)
mPullRecyclerView.setEmptyView(emptyView);
mPullRecyclerView.setEmptyView(R.layout.layout_empty_view);
2.有Header时也展示的空页面(两种方法任选其一)
// 第一个参数为true表示有Header时也显示空页面,不设置默认为false
mPullRecyclerView.setEmptyView(true, emptyView);
mPullRecyclerView.setEmptyView(true, R.layout.layout_empty_view);
四. BaseRecyclerAdapter和BaseViewHolder的使用
BaseRecyclerAdapter非常好用,它继承于RecyclerView.Adapter,配套BaseViewHolder,当我们需要一个adapter时,只需要继承它,实现其抽象方法convert(...),然后我们仅仅需要关心如何在这个方法中填充View的内容就够了,其他的都无需操心。
public class CommonListAdapter extends BaseRecyclerAdapter<CheesesItem> {
public CommonListAdapter(Context context, int layoutResId, List<CheesesItem> data) {
super(context, layoutResId, data);
}
@Override
protected void convert(final BaseViewHolder holder, final CheesesItem item) {
ImageView avatarView = holder.getView(R.id.avatar);
Glide.with(mContext)
.load(item.avatar)
.fitCenter()
.into(avatarView);
holder.setText(android.R.id.text1, item.name);
holder.getView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(holder.getView(), item.name, Snackbar.LENGTH_SHORT).show();
}
});
}
}
convert(...)方法会传递进来一个BaseViewHolder,我们通过这个BaseViewHolder可以很方便地找到需要填充的View进行填充,无需关心复用的问题,BaseViewHolder内部已经帮忙搞定。
// 通过id找到我们需要的View,无需强转
ImageView avatarView = holder.getView(R.id.avatar);
// 获取当前整个Item的View对象
holder.getView();
// 一些简单的属性可以直接设置
holder.setText(textViewId, item.name);
holder.setImageResource(ImageViewId, imageResId);
holder.setBackgroundColor(viewId, color);
BaseRecyclerAdapter中也提供了一些操作数据源的方法
// 获取当前数据源
mAdapter.getCount();
// 向当前数据源中添加一个元素并刷新(内部会调用notifyItemInserted)
mAdapter.add(...);
// 向当前数据源添加一组元素并刷新列表
mAdapter.addAll(...);
// 清空当前数据源并刷新列表
mAdapter.clear();
// 更多方法请自行查看
……
如果需要在填充View时使用到item的position,可以使用convert(...)的重载方法
@Override
protected void convert(BaseViewHolder holder, CheesesItem item, int position) {
}
上面convert(...)中传来的position是考虑了存在header的情况的,如果列表没有header,也可以直接使用holder.getLayoutPosition()方法来获取当前item的position。
感谢
1.Stay大神的课程;
2.BaseRecyclerViewAdapterHelper项目;
3.列表模拟数据是从cheesesquare项目中借用的