【Android】RecyclerView拖动排序和侧滑删除功能实现

本文介绍了如何使用ItemTouchHandler在RecyclerView中实现拖动排序和侧滑删除功能,以及第三方库SwipeDelMenuLayout的使用方法,展示了它们的实现原理和简单示例。
摘要由CSDN通过智能技术生成


简介

ItemTouchHandler 是 Google 提供的一个工具类,主要针对 RecyclerView 的上下左右拖动事件进行处理,可以同时实现拖动排序和侧滑删除功能。涉及一些事件分发的知识,感兴趣可以去分析源码。

第三方SwipeDelMenuLayout,毫无耦合性,一个Item根布局搞定 item侧滑删除菜单。不依赖任何父布局,不是针对 RecyclerView、ListView,而是任意的 ViewGroup 里的 childView 都可以使用侧滑删除。


一、ItemTouchHandler 拖动排序和侧滑删除

效果展示:

在这里插入图片描述

  1. 先创建一个回调类:ItemTouchHelperCallBack
  • context:上下文
  • listData:列表数据
  • adapter:对应适配器
  • isDrag:是否可以拖拽
  • isSwipe:是否可以侧滑
class ItemTouchHelperCallBack <T> (val context: Context,val listData:List<T>,val adapter:BaseQuickAdapter<T,BaseViewHolder>,val isDrag:Boolean=true,val isSwipe:Boolean=true): ItemTouchHelper.Callback()
  1. 实现方法(这些方法也可以设置接口在外部去实现)
  • getMovementFlags:一个是dragFlags,表示拖动效果支持的方向,另一个是swipeFlags,表示侧滑效果支持的方向
override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        var dragFlag = 0
        if (recyclerView.layoutManager is GridLayoutManager) {
            dragFlag =
                ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        } else if (recyclerView.layoutManager is LinearLayoutManager) {
            dragFlag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        }
        return makeMovementFlags(dragFlag, 0)
    }
  • onMove:当拖动开始时会回调此方法,通常将拖动的item与其他item交换位置,更新数据源
override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {

        //absoluteAdapterPosition和layoutPosition相差不大:
        //  layoutPosition相对于实际展示的布局:使用场景一般在notifyItemInserted之后, Layout不能马上获取到新的position, 布局还没更新时
        //  absoluteAdapterPosition相对于数据

        val fromPosition = viewHolder.absoluteAdapterPosition
        //拿到当前拖拽到的item的viewHolder
        val toPosition = target.absoluteAdapterPosition
        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
            	//交换
                Collections.swap(listData, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                Collections.swap(listData, i, i - 1)
            }
        }
        adapter.notifyItemMoved(fromPosition, toPosition);
        return true
    }
  • onSwiped(可自行实现):当侧滑开始时调用,从数据源里面移除相应的数据
  • onSelectedChanged :长按选中Item的时候开始调用,设置震动效果
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            //获取系统震动服务 震动70毫秒
            val vib = context.getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator
            vib.vibrate(50)
        }
        super.onSelectedChanged(viewHolder, actionState)
    }
  • clearView:手指松开的时候调用,刷新数据
override fun clearView(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ) {
        super.clearView(recyclerView, viewHolder)
        Handler().post(Runnable {
            adapter.notifyDataSetChanged(); //完成拖动后刷新适配器,这样拖动后删除就不会错乱
        })

    }
  • isLongPressDragEnabled、isItemViewSwipeEnabled
/**
     * 是否支持长按拖拽,默认为true,表示支持长按拖拽
     * 对应长按移动位置功能
     * 也可以返回false,手动调用startDrag()方法启动拖拽
     */
    override fun isLongPressDragEnabled(): Boolean {
        return isDrag
    }

    /**
     * 是否支持任意位置触摸事件发生时启用滑动操作,默认为true,表示支持滑动
     * 对应滑动删除功能
     * 也可以返回false,手动调用startSwipe()方法启动滑动
     */
    override fun isItemViewSwipeEnabled(): Boolean {
        return isSwipe
    }

全部代码:

class ItemTouchHelperCallBack <T> (val context: Context,val listData:List<T>,val adapter:BaseQuickAdapter<T,BaseViewHolder>,val isDrag:Boolean=true,val isSwipe:Boolean=true): ItemTouchHelper.Callback() {
    //一个是dragFlags,表示拖动效果支持的方向,另一个是swipeFlags,表示侧滑效果支持的方向
    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        var dragFlag = 0
        if (recyclerView.layoutManager is GridLayoutManager) {
            dragFlag =
                ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        } else if (recyclerView.layoutManager is LinearLayoutManager) {
            dragFlag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        }
        return makeMovementFlags(dragFlag, 0)
    }


    //当拖动效果已经产生了,会回调此方法,通常会更新数据源
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {

        //absoluteAdapterPosition和layoutPosition相差不大:
        //  layoutPosition相对于实际展示的布局:使用场景一般在notifyItemInserted之后, Layout不能马上获取到新的position, 布局还没更新时
        //  absoluteAdapterPosition相对于数据

        val fromPosition = viewHolder.absoluteAdapterPosition
        //拿到当前拖拽到的item的viewHolder
        val toPosition = target.absoluteAdapterPosition
        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
                Collections.swap(listData, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                //交换
                Collections.swap(listData, i, i - 1)
            }
        }
        adapter.notifyItemMoved(fromPosition, toPosition);
        return true

        //滑动事件 下面注释的代码,滑动后数据和条目错乱,被舍弃
        //Collections.swap(datas,viewHolder.getAdapterPosition(),target.getAdapterPosition());
        //ap.notifyItemMoved(viewHolder.getAdapterPosition(),target.getAdapterPosition());
    }

    //当侧滑效果以上产生了,从数据源里面移除相应的数据
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

    }

    /**
     * 长按选中Item的时候开始调用
     * 长按高亮
     * @param viewHolder
     * @param actionState
     */
    override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            //获取系统震动服务 震动70毫秒
            val vib = context.getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator
            vib.vibrate(50)
        }
        super.onSelectedChanged(viewHolder, actionState)
    }

    /**
     * 手指松开的时候还原高亮
     * @param recyclerView
     * @param viewHolder
     */
    override fun clearView(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ) {
        super.clearView(recyclerView, viewHolder)
        Handler().post(Runnable {
            adapter.notifyDataSetChanged(); //完成拖动后刷新适配器,这样拖动后删除就不会错乱
        })

    }

    /**
     * 是否支持长按拖拽,默认为true,表示支持长按拖拽
     * 对应长按移动位置功能
     * 也可以返回false,手动调用startDrag()方法启动拖拽
     */
    override fun isLongPressDragEnabled(): Boolean {
        return isDrag
    }

    /**
     * 是否支持任意位置触摸事件发生时启用滑动操作,默认为true,表示支持滑动
     * 对应滑动删除功能
     * 也可以返回false,手动调用startSwipe()方法启动滑动
     */
    override fun isItemViewSwipeEnabled(): Boolean {
        return isSwipe
    }
}
  1. 使用

先设置isDrag为false,指定某个控件再去启动拖动

private val itemTouchHelper by lazy {
        //数据listData会改变
        ItemTouchHelper(
            ItemTouchHelperCallBack(
                context = this,
                listData = itemData,
                adapter = itemAdapter,
                isDrag = false
            )
        )
    }
//与recyclerView控件绑定
itemTouchHelper.attachToRecyclerView(rv_add_item)
//指定item里面的某个控件长按才能拖动
adapter.setLongClickInterface(object :SetShowTabAdapter.LongClickInterface{
            override fun onDrag(holder: BaseViewHolder) {
                itemTouchHelper.startDrag(holder)
            }
        })

二、SwipeDelMenuLayout 侧滑删除

效果展示:

在这里插入图片描述

  1. 导入依赖
 implementation 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.3.0'
  1. xml设置

在适配器的item布局中,给需要滑动删除那个控件外层添加一个 SwipeMenuLayout 标签包裹住控件和一个删除按钮,让控件宽度铺满屏幕

<com.mcxtzhang.swipemenulib.SwipeMenuLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:orientation="horizontal"
                android:paddingStart="10dp"
                android:paddingEnd="10dp"
                android:paddingTop="15dp"
                android:paddingBottom="15dp">

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/tv_title"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:text="实例"
                        android:textColor="@color/api_grey_deep1"
                        android:textSize="16sp" />
                </LinearLayout>
            </LinearLayout>
            <Button
                android:id="@+id/btn_delete"
                android:layout_width="60dp"
                android:layout_height="match_parent"
                android:background="#ff0000"
                android:text="删除"
                android:textSize="16sp"
                android:textColor="@android:color/white"/>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
  1. 使用
    使用非常简单,只需要在适配器配置相应的点击事件即可
holder.getView<Button>(R.id.btn_delete).setOnClickListener {
            if(holder.absoluteAdapterPosition in 0 until itemCount){
                list.removeAt(holder.absoluteAdapterPosition) //移除数据
                notifyItemRemoved(holder.absoluteAdapterPosition) //刷新adapter
            }
        }

总结

实现这两个功能有很多方法,都是比较简单的。


很长时间没更新了,之前去备考了,希望能有个好结果吧

  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
RecyclerView侧滑菜单可以通过ItemTouchHelper类来实现。 首先,需要创建一个实现ItemTouchHelper.Callback的类,重写其中的方法,用于处理拖拽和侧滑等操作。在其中,我们需要实现onSwiped方法,用于处理RecyclerView中的侧滑操作。 可以通过实现onChildDraw方法来绘制侧滑菜单,例如使用Canvas绘制背景和图标等。然后在onSwiped方法中,通过ViewHolder.getAdapterPosition()获取当前侧滑的位置,然后再调用Adapter的remove方法来删除对应的数据。最后,需要在Adapter中实现onCreateViewHolder方法,在其中绑定侧滑菜单的布局和操作。 下面是一个简单的示例代码: ```java public class SwipeController extends ItemTouchHelper.Callback { private RecyclerView.Adapter adapter; public SwipeController(RecyclerView.Adapter adapter) { this.adapter = adapter; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { return makeMovementFlags(0, ItemTouchHelper.LEFT); } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); adapter.notifyItemRemoved(position); adapter.notifyItemRangeChanged(position, adapter.getItemCount()); } @Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { View itemView = viewHolder.itemView; Paint p = new Paint(); if (dX < 0) { p.setColor(Color.RED); RectF background = new RectF((float)itemView.getRight() + dX, (float)itemView.getTop(), (float)itemView.getRight(), (float)itemView.getBottom()); c.drawRect(background, p); Drawable icon = ContextCompat.getDrawable(adapter.getContext(), R.drawable.ic_delete); int iconWidth = icon.getIntrinsicWidth(); int iconHeight = icon.getIntrinsicHeight(); int left = itemView.getRight() - iconWidth - 32; int top = itemView.getTop() + (itemView.getHeight() - iconHeight) / 2; int right = itemView.getRight() - 32; int bottom = top + iconHeight; icon.setBounds(left, top, right, bottom); icon.draw(c); } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } ``` 在Activity或Fragment中,可以将SwipeController作为参数传递给ItemTouchHelper,然后调用ItemTouchHelper.attachToRecyclerView方法来绑定RecyclerView。 ```java RecyclerView recyclerView = findViewById(R.id.recycler_view); Adapter adapter = new Adapter(data); recyclerView.setAdapter(adapter); SwipeController swipeController = new SwipeController(adapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController); itemTouchHelper.attachToRecyclerView(recyclerView); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

full courage

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值