ItemTouchHelper实现短按拖拽

前言

ItemTouchHelper是一个RecyclerView的工具类,目的是为简化RecyclerView拖拽、侧滑功能的开发工作。网络搜索可以找到其具体用法。因为ItemTouchHelper默认是长按之后才能拖拽RecyclerView中的item,并且不能通过配置改变这种情况,所以我这里跟大家分享一下实现短按item拖拽的方式。

分析源码

RecyclerView版本:24.0.0

ItemTouchHelper的用法一般如下:

ItemTouchHelper helper = new ItemTouchHelper(new Callback());
helper.attachToRecyclerView(rv);

其attachToRecyclerView方法实现如下,其中我们要关注的点是对setupCallbacks方法的调用。

public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
        ...
        setupCallbacks();
        ...
}

setupCallbacks方法实现如下,其中调用了initGestureDetector方法,看来ItemTouchHelper是通过GestureDetector来实现拖拽功能的。

private void setupCallbacks() {
        ...
        initGestureDetector();
}

initGestureDetector实现如下,确实是通过GestureDetector实现的,我们来看看ItemTouchHelperGestureListener中对事件的处理。

private void initGestureDetector() {
        if (mGestureDetector != null) {
            return;
        }
        mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
                new ItemTouchHelperGestureListener());
}

ItemTouchHelperGestureListener 实现如下,直接在onLongPress方法中去处理拖拽事件。

private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            ...
        }
    }

一路看来可以判断出,我们无法通过代码设置来实现短按拖拽,只能通过非寻常方法来实现了。

实现方法

其实最简单的一种方法就是直接把ItemTouchHelper类复制一份,将OnLongPress中的代码复制到OnShowPress方法中去就可以实现了,但是这样做会造成代码的冗余,违背了程序设计的避免重复原则,所以我此处不采用此方法。

另一种方法就是采用反射去改变长按短按的时间,不过这两个值是在GestureDetector中的,而且是常量,无法修改,所以只能略过了。

第三种方法是将ItemTouchHelper中的GestureDetector的监听器给取出来,然后设置一个自定义的监听器进去,在自定义的监听器的短按事件onShowPress中去调用原监听器的长按方法onLongPress。我的实现代码如下,其中mDoDrag变量是为不让整个itemView都能拖拽 而加进去的,如果不需要可以删去:

public class ListenerInterceptor {
    private GestureDetector.OnGestureListener mListener2BeIntercept;
    private InterceptListener mListener2Intercept;

    private boolean mDoDrag = true;

    public ListenerInterceptor(ItemTouchHelper helper) {
        mListener2Intercept = new InterceptListener();

        try {
            Field fGesDetector = ItemTouchHelper.class.getDeclaredField("mGestureDetector");
            fGesDetector.setAccessible(true);
            Object objGesDetector = fGesDetector.get(helper);

            Field fImpl = GestureDetectorCompat.class.getDeclaredField("mImpl");
            fImpl.setAccessible(true);

            Object objImpl = fImpl.get(objGesDetector);


            Field fLis = null;
            try {
                fLis = objImpl.getClass().getDeclaredField("mListener");
            }catch (Exception e){e.printStackTrace();}

            Object oDet = null;
            if (fLis == null){
                Field fDet = objImpl.getClass().getDeclaredField("mDetector");
                fDet.setAccessible(true);

                oDet = fDet.get(objImpl);
                fLis = oDet.getClass().getDeclaredField("mListener");
            }
            fLis.setAccessible(true);

            mListener2BeIntercept = (GestureDetector.OnGestureListener) fLis.get(oDet);

            fLis.set(oDet, mListener2Intercept);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setDoDrag(boolean drag){
        mDoDrag = drag;
    }

    public class InterceptListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public void onShowPress(MotionEvent e) {
            if (mDoDrag)
                mListener2BeIntercept.onLongPress(e);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是一个示例代码,实现了一个可拖拽的按钮: ``` <template> <div class="drag-container"> <button class="drag-button" :style="{top: top + 'px', left: left + 'px'}" @mousedown="startDrag" @mousemove="doDrag" @mouseup="endDrag" > {{ buttonText }} </button> </div> </template> <script> export default { data() { return { dragging: false, mouseX: 0, mouseY: 0, top: 0, left: 0, buttonText: '拖拽按钮' } }, methods: { startDrag(event) { this.dragging = true; this.mouseX = event.clientX; this.mouseY = event.clientY; }, doDrag(event) { if (this.dragging) { const deltaX = event.clientX - this.mouseX; const deltaY = event.clientY - this.mouseY; this.top += deltaY; this.left += deltaX; this.mouseX = event.clientX; this.mouseY = event.clientY; } }, endDrag() { this.dragging = false; } } } </script> <style scoped> .drag-container { position: relative; height: 300px; } .drag-button { position: absolute; width: 100px; height: 50px; background-color: #42b983; color: #fff; border-radius: 5px; cursor: move; } </style> ``` 在这个示例中,我们使用了 Vue 组件,包含一个按钮元素,通过设置样式实现拖拽效果。具体实现方式是,在按钮元素上绑定 `mousedown` 事件,当鼠标按下时,设置 `dragging` 为 `true`,并记录当前鼠标位置。然后在 `mousemove` 事件中,如果 `dragging` 为 `true`,则计算鼠标位置变化量,并通过修改 `top` 和 `left` 样式实现拖拽效果。最后在 `mouseup` 事件中,将 `dragging` 重置为 `false`,拖拽结束。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

番茄大圣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值