android RecyclerView实现条目Item拖拽排序与滑动删除
发布时间 - 2026-01-10 23:21:53 点击率:次效果演示
需求和技术分析
- 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;
}
};
到这里就结束了,不信你去试试,源码传送门。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# recyclerview删除条目
# recyclerview拖拽排序
# android
# RecyclerView
# Android用RecyclerView实现图标拖拽排序以及增删管理
# Android recyclerview实现拖拽排序和侧滑删除
# android的RecyclerView实现拖拽排序和侧滑删除示例
# Android简单实现菜单拖拽排序的功能
# 拖拽
# 回调
# 自定义
# 绑定
# 自己的
# 最重要
# 不可以
# 这两个
# 作了
# 你去
# 没啥
# 返回值
# 都是
# 几个
# 是在
# 我在
# 第一个
# 一句
# 不需要
# 并在
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
,南京靠谱的征婚网站?
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
开心动漫网站制作软件下载,十分开心动画为何停播?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
Python自动化办公教程_ExcelWordPDF批量处理案例
javascript中闭包概念与用法深入理解
Laravel如何实现模型的全局作用域?(Global Scope示例)
如何在万网开始建站?分步指南解析
Windows家庭版如何开启组策略(gpedit.msc)?(安装方法)
网站制作大概多少钱一个,做一个平台网站大概多少钱?
个人摄影网站制作流程,摄影爱好者都去什么网站?
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
香港服务器网站推广:SEO优化与外贸独立站搭建策略
在线制作视频的网站有哪些,电脑如何制作视频短片?
Laravel如何处理CORS跨域请求?(配置示例)
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧
详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点
简单实现Android文件上传
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
JavaScript中的标签模板是什么_它如何扩展字符串功能
如何在自有机房高效搭建专业网站?
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】
千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
Linux系统运维自动化项目教程_Ansible批量管理实战
如何在建站主机中优化服务器配置?
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
零服务器AI建站解决方案:快速部署与云端平台低成本实践
Linux后台任务运行方法_nohup与&使用技巧【技巧】
电商网站制作价格怎么算,网上拍卖流程以及规则?
Swift中swift中的switch 语句
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
如何为不同团队 ID 动态生成多个“认领值班”按钮
Laravel如何实现事件和监听器?(Event & Listener实战)
如何用已有域名快速搭建网站?
Android利用动画实现背景逐渐变暗
如何选择可靠的免备案建站服务器?
Laravel如何使用Telescope进行调试?(安装和使用教程)
详解jQuery中基本的动画方法
在Oracle关闭情况下如何修改spfile的参数
Laravel如何集成Inertia.js与Vue/React?(安装配置)
Laravel如何使用Sanctum进行API认证?(SPA实战)
在线教育网站制作平台,山西立德教育官网?
如何确保西部建站助手FTP传输的安全性?
中国移动官方网站首页入口 中国移动官网网页登录
Python正则表达式进阶教程_复杂匹配与分组替换解析

