android RecyclerView实现条目Item拖拽排序与滑动删除

效果演示

需求和技术分析

RecyclerView Item拖拽排序::长按RecyclerView的Item或者触摸Item的某个按钮。 RecyclerView Item滑动删除:RecyclerView Item滑动删除:RecyclerView的Item滑动删除。

实现方案与技术

利用ItemTouchHelper绑定RecyclerView、ItemTouchHelper.Callback来实现UI更新,并且实现动态控制是否开启拖拽功能和滑动删除功能。

实现步骤

继承抽象类ItemTouchHelper,并在构造方法传入实现的ItemTouchHelper.Callback。 recyclerView绑定ItemTouchHelper:itemTouchHelper.attachToRecyclerView(recyclerView)。 自定义ItemTouchHelper.Callback的实现接口OnItemTouchCallbackListener,由外部更新RecyclerView的Item。

几个主要的布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_main" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>

这个没啥好说的了吧,就是一个RecyclerView啦。

接下来是RecyclerView的Item的布局item.xml:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="?android:listPreferredItemHeight" android:background="?selectableItemBackground"> <ImageView android:id="@+id/iv_touch" style="@style/ItemStyle" android:layout_height="match_parent" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:src="@android:drawable/alert_dark_frame" /> <CheckBox android:id="@+id/cb_item_check" style="@style/ItemStyle" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <TextView android:id="@+id/tv_name" style="@style/ItemStyle" android:layout_toEndOf="@id/cb_item_check" android:layout_toRightOf="@id/cb_item_check" /> <TextView android:id="@+id/tv_sex" style="@style/ItemStyle" android:layout_marginLeft="@dimen/dp_10" android:layout_marginStart="@dimen/dp_10" android:layout_toEndOf="@id/tv_name" android:layout_toRightOf="@id/tv_name" /> </RelativeLayout>

这个也不用解释了,到时候下载看源码,就是普通item,展示数据而已。

实现自己的DefaultItemTouchHelper:继承ItemTouchHelper

public class DefaultItemTouchHelper extends ItemTouchHelper { public DefaultItemTouchHelper(ItemTouchHelp.Callback callback) { super(callback); } }

好嘛,这个太简单了,基本上一行代码都不用写。但是这里需要一个ItemTouchHelp.Callback啊,所以我们还是要实现一个ItemTouchHelp.Callback,客观且看下文分解。

实现自己的ItemTouchHelper.Callback:继承ItemTouchHelper.Callback

这里是全文最重要的部分啦,要认真点看噢,先上代码,后解释,其他看注释和视频。

public class DefaultItemTouchHelpCallback extends ItemTouchHelper.Callback { /** * Item操作的回调 */ private OnItemTouchCallbackListener onItemTouchCallbackListener; /** * 是否可以拖拽 */ private boolean isCanDrag = false; /** * 是否可以被滑动 */ private boolean isCanSwipe = false; public DefaultItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) { this.onItemTouchCallbackListener = onItemTouchCallbackListener; } /** * 设置Item操作的回调,去更新UI和数据源 * * @param onItemTouchCallbackListener */ public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) { this.onItemTouchCallbackListener = onItemTouchCallbackListener; } /** * 设置是否可以被拖拽 * * @param canDrag 是true,否false */ public void setDragEnable(boolean canDrag) { isCanDrag = canDrag; } /** * 设置是否可以被滑动 * * @param canSwipe 是true,否false */ public void setSwipeEnable(boolean canSwipe) { isCanSwipe = canSwipe; } /** * 当Item被长按的时候是否可以被拖拽 * * @return */ @Override public boolean isLongPressDragEnabled() { return isCanDrag; } /** * Item是否可以被滑动(H:左右滑动,V:上下滑动) * * @return */ @Override public boolean isItemViewSwipeEnabled() { return isCanSwipe; } /** * 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向 * * @param recyclerView * @param viewHolder * @return */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager // flag如果值是0,相当于这个功能被关闭 int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlag = 0; // create make return makeMovementFlags(dragFlag, swipeFlag); } else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; int orientation = linearLayoutManager.getOrientation(); int dragFlag = 0; int swipeFlag = 0; // 为了方便理解,相当于分为横着的ListView和竖着的ListView if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局 swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN; dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; } else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN; swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; } return makeMovementFlags(dragFlag, swipeFlag); } return 0; } /** * 当Item被拖拽的时候被回调 * * @param recyclerView recyclerView * @param srcViewHolder 拖拽的ViewHolder * @param targetViewHolder 目的地的viewHolder * @return */ @Override public boolean onMove(RecyclerView recyclerView, ViewHolder srcViewHolder, ViewHolder targetViewHolder) { if (onItemTouchCallbackListener != null) { return onItemTouchCallbackListener.onMove(srcViewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition()); } return false; } @Override public void onSwiped(ViewHolder viewHolder, int direction) { if (onItemTouchCallbackListener != null) { onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition()); } } public interface OnItemTouchCallbackListener { /** * 当某个Item被滑动删除的时候 * * @param adapterPosition item的position */ void onSwiped(int adapterPosition); /** * 当两个Item位置互换的时候被回调 * * @param srcPosition 拖拽的item的position * @param targetPosition 目的地的Item的position * @return 开发者处理了操作应该返回true,开发者没有处理就返回false */ boolean onMove(int srcPosition, int targetPosition); } }

好,其实上面最重要的就是五个方法:

/** * 是否可以长按拖拽排序。 */ @Override public boolean isLongPressDragEnabled() {} /** * Item是否可以被滑动(H:左右滑动,V:上下滑动) */ @Override public boolean isItemViewSwipeEnabled() {} /** * 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向 */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {} /** * 当Item被拖拽的时候被回调 */ @Override public boolean onMove(RecyclerView r, ViewHolder rholer, ViewHolder tholder) {} /** * 当View被滑动删除的时候 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {}

isItemViewSwipeEnabled()返回值是否可以拖拽排序,true可以,false不可以,isItemViewSwipeEnabled()是否可以滑动删除,true可以,false不可以;这两个方法都是配置是否可以操作的。我们上面的代码中返回了一个成员变量值,并且这个值通过外部可以修改,所以提供了外部控制的方法。

onMove()当Item被拖拽排序移动到另一个Item的位置的时候被回调,onSwiped()当Item被滑动删除到不见;这两个方法是当用户操作了,来回调我们,我们就该去更新UI了。这里我们提供了一个Listener去通知外部,并且返回出去了必要的值,来降低代码耦合度。

getMovementFlags()说明一:是当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向,那我们又知道支持拖拽和滑动删除的无非就是LinearLayoutManager和GridLayoutManager了,相当于我们老早的时候用的ListView和GridView了。所以我们根据布局管理器的不同做了响应的区分。

getMovementFlags()说明二:其他都好理解,就是这里的return makeMovementFlags(dragFlag, swipeFlag);这句话是最终的返回值,也就是它决定了我们的拖拽或者滑动的方法。第一个参数是拖拽flag,第二个是滑动的flag。

重新定义DefaultItemTouchHelper

我们记得上面定义了一个DefaultItemTouchHelper,它的构造中需要传一个ItemTouchHelper.Callback,既然我们实现礼了,我们再把DefaultItemTouchHelper做个封装,使使用者更傻瓜式的调用。

public class DefaultItemTouchHelper extends YolandaItemTouchHelper { private DefaultItemTouchHelpCallback itemTouchHelpCallback; public DefaultItemTouchHelper(DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener) { super(new DefaultItemTouchHelpCallback(onItemTouchCallbackListener)); itemTouchHelpCallback = (DefaultItemTouchHelpCallback) getCallback(); } /** * 设置是否可以被拖拽 * * @param canDrag 是true,否false */ public void setDragEnable(boolean canDrag) { itemTouchHelpCallback.setDragEnable(canDrag); } /** * 设置是否可以被滑动 * * @param canSwipe 是true,否false */ public void setSwipeEnable(boolean canSwipe) { itemTouchHelpCallback.setSwipeEnable(canSwipe); } }

现在我们看到已经不需要传ItemTouchHelper.Callback给ItemTouchHelper了,只需要传我们在DefaultItemTouchHelpCallback中定义好的OnItemTouchCallbackListener就好了,而且提供了设置是否可以滑动和是否可以拖拽的方法,而OnItemTouchCallbackListener只是通知外部滑动了、删除了,你去更新UI吧。

这里可以有的同学会有疑问,上面原来不是继承ItemTouchHelper吗?这里咋就变成了YolandaItemTouchHelper了呢?因为我们看到这里多了一句itemTouchHelpCallback = getCallback();,这个getCallback();这个方法是没有的,是我们在YolandaItemTouchHelper中自定义的,因为我们想在DefaultItemTouchHelper中提供外部设置是否可以拖拽和滑动删除的方法,就得拿到这个Callback,所以我看了下源码,我们在ItemTouchHelper构造中把Callback穿进去,它保存的时候一个package级别的成员变量,所以我在Android.support.v7.widget.helper包下新建了一个YolandaItemTouchHelper类:

public class YolandaItemTouchHelper extends ItemTouchHelper { public YolandaItemTouchHelper(Callback callback) { super(callback); } public Callback getCallback() { return mCallback; } }

如何投入使用

好扯淡也扯完了,封装也封装完了,那么接下来就来在Activity中使用下咯:

recyclerView绑定ItemTouchHelper

没啥好说的用itemTouchHelper.attachToRecyclerView(recyclerView)绑定recyclerView和ItemTouchHelper,并且只是允许拖拽和滑动删除Item:

DefaultItemTouchHelper itemTouchHelper = new DefaultItemTouchHelper(onItemTouchCallbackListener); itemTouchHelper.attachToRecyclerView(recyclerView); itemTouchHelper.setDragEnable(true); itemTouchHelper.setSwipeEnable(true);

看到上面还缺少一个onItemTouchCallbackListener吧,这个也比较重要。

使用Callback自定义的OnItemTouchCallbackListener刷新UI

我们在自定义Callback的时候不是在onMove()和onSwiped()方法中回调OnItemTouchCallbackListener去更新UI吗?这里就是OnItemTouchCallbackListener如何更新UI的操作了,完成这个操作,那么我们的目的就达到了:

private DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener = new DefaultItemTouchHelpCallback.OnItemTouchCallbackListener() { @Override public void onSwiped(int adapterPosition) { // 滑动删除的时候,从数据源移除,并刷新这个Item。 if (userInfoList != null) { userInfoList.remove(adapterPosition); mainAdapter.notifyItemRemoved(adapterPosition); } } @Override public boolean onMove(int srcPosition, int targetPosition) { if (userInfoList != null) { // 更换数据源中的数据Item的位置 Collections.swap(userInfoList, srcPosition, targetPosition); // 更新UI中的Item的位置,主要是给用户看到交互效果 mainAdapter.notifyItemMoved(srcPosition, targetPosition); return true; } return false; } };

到这里就结束了,不信你去试试,源码传送门。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-10-09 16:10:40

android RecyclerView实现条目Item拖拽排序与滑动删除的相关文章

Android recyclerview实现拖拽排序和侧滑删除

Recyclerview现在基本已经替代Listview了,RecyclerView也越来越好用了  当我们有实现条目的拖拽排序和侧滑删除时  可以直接时候Recyclerview提供的API就可以直接实现了 先贴上主要代码 private void initveiw() { ArrayList<String> items = new ArrayList<>(Arrays.asList("itme1", "item2", "itme

Android中在GridView网格视图上实现item拖拽交换的方法_Android

GridView基础新建一个HelloGridView的工程 修改main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gridview" android:layout_width=&qu

Android中在GridView网格视图上实现item拖拽交换的方法

GridView基础 新建一个HelloGridView的工程 修改main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gridview" android:layout_width=&q

iOS_UI进阶【拖拽排序】的实现

导读 拖拽排序是新闻类的App可以说是必有的交互设计,如今日头条,网易新闻等.拖拽排序是一个交互体验非常好的设计,简单,方便. github地址:https://github.com/HelloYeah/DraggingSort 欢迎Star,予人玫瑰,手有余香. 今日头条的拖拽排序界面 我实现的长按拖拽排序效果 实现方案 1.给CollectionViewCell添加一个长按手势,通过协议把手势传递到collectionView所在的控制器中. - (void)awakeFromNib{ se

Sortable.js拖拽排序使用方法解析_javascript技巧

最近公司项目经常用到一个拖拽 Sortable.js插件,所以有空的时候看了 Sortable.js 源码,总共1300多行这样,写的挺完美的. 官网: http://rubaxa.github.io/Sortable/ 拖拽的时候主要由这几个事件完成,     ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上     ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上     ondragover 事件:拖拽

jQuery拖拽排序插件制作拖拽排序效果(附源码下载)_jquery

使用jquery拖拽排序插件制作拖拽排序效果是一款非常实用的鼠标拖拽布局插件.效果图如下: 效果演示         源码下载 html代码: <h1>水平拖拽</h1> <div class="demo"> <div class="item item1"><span>1</span></div> <div class="item item2"><

dhtmlxTree目录树增加右键菜单以及拖拽排序的实现方法_php实例

在以前的一个公司内部管理系统(InnerOA)中,对于目录树的构造我采用的是dTree,实现无限级目录显示及右键菜单功能(右键菜单中包括:新建.修改.共享.删除.刷新等功能,如下图所示) 关于公司内部管理系统(InnerOA)中目录树的一些知识以后有时间将整理并提供源码. 但是dTree唯一遗憾的是不支持拖拽排序功能,这让我在完成InnerOA之后心里一直纠结的问题.在网上查看关于目录树的一些内容,dTree是我目前认为最符合我项目的一个.在一个偶然机会,发现了另一个强大的目录树,也就是本文所说

jquery 拖拽排序插件Sortable使用方法

Sortable是一个独立的JS插件,不需要jquery,Sortable非常轻量,压缩后只有2KB,适用现代浏览器与移动设备.使用Sortable可以轻松实现鼠标拖拽排序,让用户操作更方便,具有极强的体验性. 特点 支持触摸设备和现代浏览器 使用本地化HTML5 API拖拽 简单的API 轻量级的,压缩只有2 kb 无需jQuery 使用方法 引入核心文件Sortable <script src="Sortable.js"></script> 构建html,不

jquery实现图片拖拽排序

最近在研究Interface elements for jQuery(http://interface.eyecon.ro/),此插件封装了一些拖拽效果,并且使用非常简单,能轻松实现拖拽排序.购物车.博客首页排版等UI,所以模仿和讯的图片排序做了一个简单样例: 代码: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /&g