LRecyclerView是支持addHeaderView、 addFooterView、下拉刷新、分页加载数据的RecyclerView。
新增功能:SwipeMenu系列功能,包括Item侧滑菜单、长按拖拽Item,滑动删除Item等。
它对 RecyclerView 控件进行了拓展,给RecyclerView增加HeaderView、FooterView,并且不需要对你的Adapter做任何修改。
Step 1. 在你的根build.gradle文件中增加JitPack仓库依赖。
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
Step 2. 在你的model的build.gradle文件中增加LRecyclerView依赖。
compile 'com.github.jdsjlzx:LRecyclerView:1.0.6'
LRecyclerView requires at minimum Java 7 or Android 4.0.
##项目简述
- 下拉刷新、滑动到底部自动加载下页数据;
- 可以方便添加Header和Footer;
- 头部下拉样式可以自定义;
- 具备item点击和长按事件;
- 网络错误加载失败点击Footer重新请求数据;
- 可以动态为FooterView赋予不同状态(加载中、加载失败、滑到最底等);
- SwipeMenu系列功能,包括Item侧滑菜单、长按拖拽Item,滑动删除Item等;
- 实现viewpager的功能。
注意:EndlessLinearLayoutActivity.java类里面有标准完整的使用方法,请尽量在这个界面看效果。
##Demo下载 点我下载
##功能介绍
//add a HeaderView
RecyclerViewUtils.setHeaderView(mRecyclerView, new SampleHeader(this));
//add a FooterView
RecyclerViewUtils.setFooterView(mRecyclerView, new SampleFooter(this));
添加HeaderView还可以使用下面两种方式:
View header = LayoutInflater.from(this).inflate(R.layout.sample_header,(ViewGroup)findViewById(android.R.id.content), false);
RecyclerViewUtils.setHeaderView(mRecyclerView, header);
CommonHeader headerView = new CommonHeader(getActivity(), R.layout.layout_home_header);
RecyclerViewUtils.setHeaderView(mRecyclerView, headerView);
上面的方式同样适用于FooterView。
LScrollListener实现了nRefresh()、onScrollUp()、onScrollDown()、onBottom()、onScrolled五个事件,如下所示:
void onRefresh();//pull down to refresh
void onScrollUp();//scroll down to up
void onScrollDown();//scroll from up to down
void onBottom();//load next page
void onScrolled(int distanceX, int distanceY);// moving state,you can get the move distance
- onRefresh()——RecyclerView下拉刷新事件;
- onScrollUp()——RecyclerView向上滑动的监听事件;
- onScrollDown()——RecyclerView向下滑动的监听事件;
- onBottom()——RecyclerView滑动到底部的监听事件;
- onScrollDown()——RecyclerView正在滚动的监听事件;
使用:
mRecyclerView.setLScrollListener(new LRecyclerView.LScrollListener() {
@Override
public void onRefresh() {
//下拉刷新
}
@Override
public void onScrollUp() {
}
@Override
public void onScrollDown() {
}
@Override
public void onBottom() {
// 加载更多(加载下页数据)
}
@Override
public void onScrolled(int distanceX, int distanceY) {
}
});
###下拉刷新和加载更多
详见LScrollListener接口介绍。
####设置下拉刷新样式
mRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader); //设置下拉刷新Progress的样式
mRecyclerView.setArrowImageView(R.drawable.iconfont_downgrey); //设置下拉刷新箭头
AVLoadingIndicatorView库有多少效果,LRecyclerView就支持多少下拉刷新效果,当然你也可以自定义下拉刷新效果。
效果图:
####设置加载更多样式
拷贝LRecyclerview_library中的文件文件layout_recyclerview_list_footer_loading.xml到你的工程中,修改后即可。
###开启和禁止下拉刷新功能
mRecyclerView.setPullRefreshEnabled(true);
or
mRecyclerView.setPullRefreshEnabled(false);
默认是开启。
###强制刷新
根据大家的反馈,增加了一个强制刷新的方法,使用如下:
mRecyclerView.forceToRefresh();
无论是下拉刷新还是强制刷新,刷新完成后调用下面代码:
mRecyclerView.refreshComplete();
mLRecyclerViewAdapter.notifyDataSetChanged();
###下拉刷新清空数据 有的时候,需要下拉的时候情况数据并更新UI,可以这么做:
@Override
public void onRefresh() {
RecyclerViewStateUtils.setFooterViewState(mRecyclerView,LoadingFooter.State.Normal);
mDataAdapter.clear();
mCurrentCounter = 0;
isRefresh = true;
requestData();
}
如果不需要下拉的时候情况数据并更新UI,如下即可:
@Override
public void onRefresh() {
isRefresh = true;
requestData();
}
加载数据时如果网络异常或者断网,LRecyclerView为你提供了重新加载的机制。
效果图:
网络异常出错代码处理如下:
RecyclerViewStateUtils.setFooterViewState(getActivity(), mRecyclerView, getPageSize(), LoadingFooter.State.NetWorkError, mFooterClick);
private View.OnClickListener mFooterClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
RecyclerViewStateUtils.setFooterViewState(getActivity(), mRecyclerView, getPageSize(), LoadingFooter.State.Loading, null);
requestData();
}
};
上面的mFooterClick就是我们点击底部的Footer时的逻辑处理事件,很显然我们还是在这里做重新请求数据操作。
先看下怎么使用:
mLRecyclerViewAdapter.setOnItemClickLitener(new OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
ItemModel item = mDataAdapter.getDataList().get(position);
Toast.makeText(EndlessLinearLayoutActivity.this, item.title, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
ItemModel item = mDataAdapter.getDataList().get(position);
Toast.makeText(EndlessLinearLayoutActivity.this, "onItemLongClick - " + item.title, Toast.LENGTH_SHORT).show();
}
});
原理就是实现viewHolder.itemView的点击和长按事件。由于代码过多就不贴出来了。
viewHolder源码如下:
public static abstract class ViewHolder {
public final View itemView;
int mPosition = NO_POSITION;
int mOldPosition = NO_POSITION;
long mItemId = NO_ID;
int mItemViewType = INVALID_TYPE;
int mPreLayoutPosition = NO_POSITION;
###设置空白View(setEmptyView)
mRecyclerView.setEmptyView(view);
###SwipeMenuAdapter
为了实现SwipeMenu的功能,此次新增了一个SwipeMenuAdapter类。
SwipeMenuAdapter与library中已经存在的LRecyclerViewAdapter会不会冲突呢?答案是不会。SwipeMenuAdapter是用户级别的基类adapter,也就是用户需要继承SwipeMenuAdapter去实现自己的adapter,具体使用同以前。
SwipeMenuAdapter类的定义:
public abstract class SwipeMenuAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH>
实现自己的MenuAdapter:
public class MenuAdapter extends SwipeMenuAdapter<MenuAdapter.DefaultViewHolder> {
protected List<ItemModel> mDataList = new ArrayList<>();
public MenuAdapter() {
}
@Override
public int getItemCount() {
return mDataList.size();
}
@Override
public View onCreateContentView(ViewGroup parent, int viewType) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_text_swipe, parent, false);
}
@Override
public MenuAdapter.DefaultViewHolder onCompatCreateViewHolder(View realContentView, int viewType) {
return new DefaultViewHolder(realContentView);
}
@Override
public void onBindViewHolder(MenuAdapter.DefaultViewHolder holder, int position) {
String item = mDataList.get(position).title;
DefaultViewHolder viewHolder = holder;
viewHolder.tvTitle.setText(item);
}
static class DefaultViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
public DefaultViewHolder(View itemView) {
super(itemView);
tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
}
}
}
是不是很方便?MenuAdapter基本的功能都满足了,直接拷贝到项目中即可使用。
###左右两侧都有菜单
效果图:
具体使用步骤如下。
- 为SwipeRecyclerView的Item创建菜单
// 设置菜单创建器。
mRecyclerView.setSwipeMenuCreator(swipeMenuCreator);
//设置菜单Item点击监听事件
mRecyclerView.setSwipeMenuItemClickListener(menuItemClickListener);
swipeMenuCreator完成了左右菜单的创建,menuItemClickListener实现了菜单的点击事件。
需要注意的是,LRecyclerView提供了下面两个方法,具体使用请详见demo。
public void openLeftMenu(int position, int duration) {
openMenu(position, LEFT_DIRECTION, duration);
}
public void openRightMenu(int position) {
openMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
}
openLeftMenu:打开item的左边菜单 openRightMenu:打开item的右边菜单
这里关键的就是这个position(详细请参考demo)。
###根据ViewType显示菜单
效果图:
根据ViewType决定SwipeMenu在哪一行出现,可以左侧,可以右侧。
自定义MenuViewTypeAdapter,代码如下:
public class MenuViewTypeAdapter extends MenuAdapter {
public static final int VIEW_TYPE_MENU = 1;
public static final int VIEW_TYPE_NONE = 2;
@Override
public int getItemViewType(int position) {
return position % 2 == 0 ? VIEW_TYPE_MENU : VIEW_TYPE_NONE;
}
}
在实现swipeMenuCreator 时,需要根据ItemViewType值来决定是否创建左右菜单。
private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() {
@Override
public void onCreateMenu(SwipeMenu swipeLeftMenu, SwipeMenu swipeRightMenu, int viewType) {
// 根据Adapter的ViewType来决定菜单的样式、颜色等属性、或者是否添加菜单。
if (viewType == MenuViewTypeAdapter.VIEW_TYPE_NONE) {
// Do nothing.
} else if (viewType == MenuViewTypeAdapter.VIEW_TYPE_MENU) {
int size = getResources().getDimensionPixelSize(R.dimen.item_height);
......
}
}
};
###长按拖拽Item(List),与菜单结合
效果图:
关键代码:
mRecyclerView.setLongPressDragEnabled(true);// 开启拖拽功能
mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI。
onItemMoveListener具体如下:
/**
* 当Item移动的时候。
*/
private OnItemMoveListener onItemMoveListener = new OnItemMoveListener() {
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition);
final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition);
// 当Item被拖拽的时候。
Collections.swap(mDataAdapter.getDataList(), adjFromPosition, adjToPosition);
//Be carefull in here!
mLRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition);
return true;// 返回true表示处理了,返回false表示你没有处理。
}
@Override
public void onItemDismiss(int position) {
// 当Item被滑动删除掉的时候,在这里是无效的,因为这里没有启用这个功能。
// 使用Menu时就不用使用这个侧滑删除啦,两个是冲突的。
}
};
注意下面代码:
final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition);
final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition);
关于position的位置,为了大家使用方便,特在LRecyclerViewAdapter中提供了一个方法getAdapterPosition(boolean isCallback, int position)。
- isCallback 含义:position是否接口回调中带来的
- position 含义:如果不是接口回调,就是用户自己指定的position
- getAdapterPosition(boolean isCallback, int position)只用于非LRecyclerViewAdapter提供的接口。
举例说明:
- setOnItemMoveListener不是 LRecyclerViewAdapter自带接口(也就是内部方法),需要调用getAdapterPosition方法获得正确的position
- 如setOnItemClickLitener 是 LRecyclerViewAdapter自带接口,接口里面自带了position,用户就不必调用getAdapterPosition方法,直接使用就可以了。
mLRecyclerViewAdapter.setOnItemClickLitener(new OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
String text = "Click position = " + position;
}
@Override
public void onItemLongClick(View view, int position) {
}
});
###长按拖拽Item(Grid)
效果图:
与list功能一样,只是布局不一样。
###滑动直接删除Item
效果图:
注意:
滑动删除和滑动菜单是互相冲突的,两者只能出现一个。
关键代码:
mRecyclerView.setLongPressDragEnabled(true);
mRecyclerView.setItemViewSwipeEnabled(true);// 开启滑动删除 mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI
按照配置就可以实现滑动删除。
###指定某个Item不能拖拽或者不能滑动删除
效果图:
关键代码:
mRecyclerView.setLongPressDragEnabled(true);
mRecyclerView.setItemViewSwipeEnabled(true);// 开启滑动删除。 mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI。 mRecyclerView.setOnItemMovementListener(onItemMovementListener);
###用SwipeMenuLayout实现你自己的侧滑
效果图:
这个与LRecyclerView关系不大,但是与SwipeMenu关系密切。为了实现滑动菜单的功能,定义了SwipeMenuLayout,详细使用见demo。
##分组
效果图:
功能还在完善中....
##代码混淆
#LRecyclerview_library
-dontwarn com.github.jdsjlzx.**
-keep class com.github.jdsjlzx.**{*;}
如果你想了解更多混淆配置,参考:http://blog.csdn.net/jdsjlzx/article/details/51861460
##注意事项
- 如果添加了footerview,不要再使用setLScrollListener方法,如有需要,自定义实现即可。如下面代码不要同时使用:
mRecyclerView.setLScrollListener(LScrollListener);
RecyclerViewUtils.setFooterView(mRecyclerView, new SampleFooter(this));
- 不要SwipeRefreshLayout与LRecyclerView一起使用,会有冲突。
##Thanks
##打赏
觉得本框架对你有帮助,不妨打赏赞助我一下,让我有动力走的更远。