本文共 13838 字,大约阅读时间需要 46 分钟。
2016-09-07 一叶飘舟本篇来自 一叶飘舟 (也是一位csdn大神)再次投稿,在他原作的基础上进行了大幅度升级,之前就很受大家欢迎,希望依旧给你们带来惊喜。
读完本文大概需要8分钟~~~eh~~~或许还要再久点。。。
一叶飘舟 的博客地址:
LRecyclerView能做什么http://blog.csdn.net/jdsjlzx
如果你之前没有听说过 LRecyclerView,那么请点击作者之前推文(之前取名 SuperRecyclerView,但是github有重名项目,故现改为LRecyclerView):
经过再三思考,同时也为了大家使用方便,LRecyclerView 集成了 SwipeMenu 系列功能,包括 Item侧滑菜单、长按拖拽Item,滑动删除Item 等功能。
demo apk下载地址:
功能演示https://raw.githubusercontent.com/jdsjlzx/LRecyclerView/master/app/app-release.apk
本次新增 SwipeMenu 系列功能描述如下:
左右两侧都有菜单;
根据ViewType显示菜单;
长按拖拽Item(List),与菜单结合;
长按拖拽Item(Grid);
滑动删除Item;
指定某个Item不能拖拽或者不能滑动删除;
用SwipeMenuLayout实现你自己的侧滑。
项目地址:
https://github.com/jdsjlzx/LRecyclerView
SwipeMenuAdapter
为了实现 SwipeMenu 的功能,此次新增了一个 SwipeMenuAdapter 类。
SwipeMenuAdapter 与 library 中已经存在的 LRecyclerViewAdapter会不会冲突呢?答案是不会。SwipeMenuAdapter是用户级别的基类adapter,也就是用户需要继承SwipeMenuAdapter去实现自己的adapter,还像之前那样使用即可。
SwipeMenuAdapter 类的定义:
public abstract class SwipeMenuAdapterextends RecyclerView.Adapter
实现自己的 MenuAdapter:
是不是很方便?MenuAdapter 基本的功能都满足了,直接拷贝到项目中即可使用。
上面说了那么多,关键的也就这几句:
mDataAdapter = new MenuAdapter();mDataAdapter.setDataList(dataList);mLRecyclerViewAdapter = new LRecyclerViewAdapter(this, mDataAdapter);mRecyclerView.setAdapter(mLRecyclerViewAdapter);
下面具体分析每个功能。
左右两侧菜单
效果图:
具体使用步骤如下:
为 SwipeRecyclerView 的 Item 创建菜单
// 设置菜单创建器mRecyclerView.setSwipeMenuCreator(swipeMenuCreator);// 设置菜单Item点击监听事件mRecyclerView.setSwipeMenuItemClickListener(menuItemClickListener);
其中 swipeMenuCreator 和 menuItemClickListener 代码如下:
/** * 菜单创建器。在Item要创建菜单的时候调用。 */
private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() { @Override public voidonCreateMenu(SwipeMenu swipeLeftMenu, SwipeMenu swipeRightMenu,int viewType) { int size = getResources().getDimensionPixelSize(R.dimen.item_height); // 添加左侧的,如果不添加,则左侧不会出现菜单。 { SwipeMenuItem addItem = new SwipeMenuItem(mContext) .setBackgroundDrawable(R.drawable.selector_green)// 点击的背景。 .setImage(R.mipmap.ic_action_add) // 图标。 .setWidth(size) // 宽度。 .setHeight(size); // 高度。 swipeLeftMenu.addMenuItem(addItem); // 添加一个按钮到左侧菜单。 SwipeMenuItem closeItem = new SwipeMenuItem(mContext) .setBackgroundDrawable(R.drawable.selector_red) .setImage(R.mipmap.ic_action_close) .setWidth(size) .setHeight(size); swipeLeftMenu.addMenuItem(closeItem); // 添加一个按钮到左侧菜单。 } // 添加右侧的,如果不添加,则右侧不会出现菜单。 { SwipeMenuItem deleteItem = new SwipeMenuItem(mContext) .setBackgroundDrawable(R.drawable.selector_red) .setImage(R.mipmap.ic_action_delete) .setText("删除") // 文字,还可以设置文字颜色,大小等。。 .setTextColor(Color.WHITE) .setWidth(size) .setHeight(size); swipeRightMenu.addMenuItem(deleteItem);// 添加一个按钮到右侧侧菜单。 SwipeMenuItem closeItem = new SwipeMenuItem(mContext) .setBackgroundDrawable(R.drawable.selector_purple) .setImage(R.mipmap.ic_action_close) .setWidth(size) .setHeight(size); swipeRightMenu.addMenuItem(closeItem); // 添加一个按钮到右侧菜单。 SwipeMenuItem addItem = new SwipeMenuItem(mContext) .setBackgroundDrawable(R.drawable.selector_green) .setText("添加") .setTextColor(Color.WHITE) .setWidth(size) .setHeight(size); swipeRightMenu.addMenuItem(addItem); // 添加一个按钮到右侧菜单。 } } }; /** * 菜单点击监听。 */ private OnSwipeMenuItemClickListener menuItemClickListener =new OnSwipeMenuItemClickListener() { /** * Item的菜单被点击的时候调用。 * @param closeable closeable. 用来关闭菜单。 * @param adapterPosition adapterPosition. 这个菜单所在的item在Adapter中position。 * @param menuPosition menuPosition. 这个菜单的position。比如你为某个Item创建了2个MenuItem,那么这个position可能是是 0、1, * @param direction 如果是左侧菜单,值是:SwipeMenuRecyclerView#LEFT_DIRECTION,如果是右侧菜单,值是:SwipeMenuRecyclerView#RIGHT_DIRECTION. */ @Override public voidonItemClick(Closeable closeable, int adapterPosition, int menuPosition, int direction) { closeable.smoothCloseMenu();// 关闭被点击的菜单。 if (direction == LRecyclerView.RIGHT_DIRECTION) { Toast.makeText(mContext, "list第" + adapterPosition +"; 右侧菜单第" + menuPosition, Toast.LENGTH_SHORT).show(); } else if (direction == LRecyclerView.LEFT_DIRECTION) { Toast.makeText(mContext, "list第" + adapterPosition +"; 左侧菜单第" + menuPosition, Toast.LENGTH_SHORT).show(); } } };从上面代码可以看出,swipeMenuCreator 完成了左右菜单的创建,menuItemClickListener实现了菜单的点击事件。
需要注意的是,LRecyclerView 提供了下面两个方法,具体使用请详见demo。
public voidopenLeftMenu(int position,int duration) {
openMenu(position, LEFT_DIRECTION, duration); } public voidopenRightMenu(int position) { openMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION); }openLeftMenu:打开item的左边菜单
openRightMenu:打开item的右边菜单
这里关键的就是这个 position(详细请参考demo),先埋下个伏笔,后面介绍。
根据ViewType显示菜单
效果图:
根据 ViewType 决定 SwipeMenu 在哪一行出现,可以左侧,可以右侧。
自定义 MenuViewTypeAdapter,代码如下:
public classMenuViewTypeAdapter extendsMenuAdapter {
public staticfinal int VIEW_TYPE_MENU =1; public staticfinal int VIEW_TYPE_NONE =2; @Override public intgetItemViewType(int position) { return position % 2 == 0 ? VIEW_TYPE_MENU : VIEW_TYPE_NONE; } }在实现 swipeMenuCreator 时,需要根据 ItemViewType 值来决定是否创建左右菜单。
private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() {
@Override public voidonCreateMenu(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); ...... } } };长按拖拽(List),与菜单结合
效果图:
关键代码:
// 开启拖拽功能mRecyclerView.setLongPressDragEnabled(true);// 监听拖拽,更新UImRecyclerView.setOnItemMoveListener(onItemMoveListener);
onItemMoveListener 具体如下:
/** * 当Item移动的时候。 */
private OnItemMoveListener onItemMoveListener =new OnItemMoveListener() { @Override public booleanonItemMove(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 voidonItemDismiss(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 voidonItemClick(View view, int position) { String text = "Click position = " + position; } @Override public voidonItemLongClick(View view, int position) { } });
长按拖拽Item(Grid)
效果图:
与 list 功能一样,只是布局不一样。
滑动直接删除Item
效果图:
注意:滑动删除和滑动菜单是互相冲突的,两者只能出现一个。
关键代码:
mRecyclerView.setLongPressDragEnabled(true);// 开启滑动删除mRecyclerView.setItemViewSwipeEnabled(true);// 监听拖拽,更新UImRecyclerView.setOnItemMoveListener(onItemMoveListener);
按照配置就可以实现滑动删除。
指定Item不能拖拽或不能滑动删除
效果图:
关键代码:
mRecyclerView.setLongPressDragEnabled(true);// 开启滑动删除mRecyclerView.setItemViewSwipeEnabled(true);// 监听拖拽,更新UImRecyclerView.setOnItemMoveListener(onItemMoveListener);mRecyclerView.setOnItemMovementListener(onItemMovementListener);
其中,onItemMovementListener 具体实现如下:
/** * 当Item被移动之前。 */
public static OnItemMovementListener onItemMovementListener =new OnItemMovementListener() { /** * 当Item在移动之前,获取拖拽的方向。 * @param recyclerView {@link RecyclerView}. * @param targetViewHolder target ViewHolder. * @return */ @Override public intonDragFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder) { // 我们让第一个不能拖拽 if (targetViewHolder.getAdapterPosition() ==0) { return OnItemMovementListener.INVALID;// 返回无效的方向。 } RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { // 如果是LinearLayoutManager。 LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) { // 横向的List。 return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT;// 只能左右拖拽。 } else { // 竖向的List。 return OnItemMovementListener.UP | OnItemMovementListener.DOWN;// 只能上下拖拽。 } } else if (layoutManagerinstanceof GridLayoutManager) { // 如果是Grid。 return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT | OnItemMovementListener.UP | OnItemMovementListener.DOWN;// 可以上下左右拖拽。 } return OnItemMovementListener.INVALID;// 返回无效的方向。 } @Override public intonSwipeFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder) { // 我们让第一个不能滑动删除。 if (targetViewHolder.getAdapterPosition() ==0) { return OnItemMovementListener.INVALID;// 返回无效的方向。 } RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { // 如果是LinearLayoutManager LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) { // 横向的List。 return OnItemMovementListener.UP | OnItemMovementListener.DOWN;// 只能上下滑动删除。 } else { // 竖向的List。 return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT;// 只能左右滑动删除。 } } return OnItemMovementListener.INVALID;// 其它均返回无效的方向。 } };
onItemMoveListener 具体实现如下:
/** * 当Item移动的时候。 */
private OnItemMoveListener onItemMoveListener =new OnItemMoveListener() { @Override public booleanonItemMove(int fromPosition,int toPosition) { final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition); final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition); if (adjToPosition == 0) { // 保证第一个不被挤走。 return false; } // 当Item被拖拽的时候。 Collections.swap(mDataAdapter.getDataList(), adjFromPosition, adjToPosition); //Be carefull in here! mLRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition); return true; } @Override public voidonItemDismiss(int position) { final int adjPosition = mLRecyclerViewAdapter.getAdapterPosition(true, position); mDataAdapter.remove(adjPosition); AppToast.showShortText(DragSwipeFlagsActivity.this,"现在的第" + adjPosition + "条被删除。"); } };通过代码中的注释,就可以明白了,一切尽在代码中。
用SwipeMenuLayout实现侧滑
效果图:
这个与 LRecyclerView 关系不大,但是与 SwipeMenu 关系密切。为了实现滑动菜单的功能,定义了SwipeMenuLayout。
SwipeMenuLayout 类的定义:
public class SwipeMenuLayout extends FrameLayout implements SwipeSwitch
在开头提到的 SwipeMenuAdapter 的
@Override
public final VH onCreateViewHolder(ViewGroupparent, int viewType) { View contentView = onCreateContentView(parent, viewType); if (mSwipeMenuCreator != null) { SwipeMenuLayout swipeMenuLayout = (SwipeMenuLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_swipe_item_default,parent, false); ...... }layout_recyclerview_swipe_item_default.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.github.jdsjlzx.swipe.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:swipe="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" swipe:contentViewId="@+id/swipe_content" swipe:leftViewId="@+id/swipe_left" swipe:rightViewId="@+id/swipe_right"> <com.github.jdsjlzx.swipe.SwipeMenuView android:id="@id/swipe_left" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <FrameLayout android:id="@id/swipe_content" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.github.jdsjlzx.swipe.SwipeMenuView android:id="@id/swipe_right" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </com.github.jdsjlzx.swipe.SwipeMenuLayout>看来这个布局,你是不是有种恍然大悟的感觉呢?左右滑动就是通过SwipeMenuView 来实现的。
项目地址,欢迎Star:
https://github.com/jdsjlzx/LRecyclerView