Android LRecyclerView实现Item侧滑菜单长按拖拽Item滑动删除Item等功能
Posted 冰糖葫芦三剑客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android LRecyclerView实现Item侧滑菜单长按拖拽Item滑动删除Item等功能相关的知识,希望对你有一定的参考价值。
LRecyclerView能做什么?
如果你之前没有听说过LRecyclerView,那么请参考: https://github.com/jdsjlzx/LRecyclerView
经过再三思考,同时也为了大家使用方便,LRecyclerView集成了SwipeMenu系列功能,包括Item侧滑菜单、长按拖拽Item,滑动删除Item等功能。
demo 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 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();
public List<ItemModel> getDataList()
return mDataList;
public void setDataList(Collection<ItemModel> list)
this.mDataList.clear();
this.mDataList.addAll(list);
notifyDataSetChanged();
public void addAll(Collection<ItemModel> list)
int lastIndex = this.mDataList.size();
if (this.mDataList.addAll(list))
notifyItemRangeInserted(lastIndex, list.size());
public void remove(int position)
mDataList.remove(position);
notifyItemRemoved(position);
if(position != mDataList.size()) // 如果移除的是最后一个,忽略
notifyItemRangeChanged(position, mDataList.size() - position);
public void clear()
mDataList.clear();
notifyDataSetChanged();
@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基本的功能都满足了,直接拷贝到项目中即可使用。
上面说了那么多,关键的也就这几句:
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 void onCreateMenu(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 void onItemClick(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 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);
其中,onItemMovementListener具体实现如下:
/**
* 当Item被移动之前。
*/
public static OnItemMovementListener onItemMovementListener = new OnItemMovementListener()
/**
* 当Item在移动之前,获取拖拽的方向。
* @param recyclerView @link RecyclerView.
* @param targetViewHolder target ViewHolder.
* @return
*/
@Override
public int onDragFlags(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 (layoutManager instanceof GridLayoutManager) // 如果是Grid。
return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT | OnItemMovementListener.UP | OnItemMovementListener.DOWN; // 可以上下左右拖拽。
return OnItemMovementListener.INVALID;// 返回无效的方向。
@Override
public int onSwipeFlags(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 boolean onItemMove(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 void onItemDismiss(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(ViewGroup parent, 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来实现的。
项目地址:https://github.com/jdsjlzx/LRecyclerView,欢迎Star!
Thanks
如果你觉得这篇文章对你有用,那么赞一个或者留个言吧!
另外下载Demo有意外收获啊!
如果你对LRecyclerView有什么好的想法或者建议,期待你的留言!
以上是关于Android LRecyclerView实现Item侧滑菜单长按拖拽Item滑动删除Item等功能的主要内容,如果未能解决你的问题,请参考以下文章
Android:如何使用 RecyclerView 实现图片库
[Android Interface]What is it? can I create it by? [closed]
哪为IT大神帮个忙,Android客服端如何能够控制视图层监听的数据传输到服务端?
错误-Android-OpenCV-It seems that your device does not support camera(or it is locked)