在ScrollView中跨RecylerView拖拽数据的效果实现(更简单的事件冲突处理方案)

先看效果

在这里插入图片描述

再上源码

https://github.com/woshiwzy/MultiRecyclerViewDrag

先说说通常的事件处理方法

1. 拦截器方案

  • 内部拦截法 和外部拦截法

个人觉得这是最麻烦最不推荐的方法,虽然很多时候面试的时候会问,但是这种方法处理处理起来又臭又长,十分麻烦,而且不好维护。

2. 事件主动转发或者主动产生事件

1.主动转发,即拦ViewGroup所有的事件t 然后调用dispatchTouchEvent,转发给你想要接受事件的View

2.主动产生事件,再转发(eg如下,主动产生一个Action_Cancel事件)

     //手动触发一个cancel事件
        long downTime = SystemClock.uptimeMillis();
        long eventTime = SystemClock.uptimeMillis() + 100;
        int metaState = 0;
        int x = 0, y = 0;
        MotionEvent motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, x, y, metaState);
        dispatchTouchEvent(motionEvent);

3.通过修改ViewGroup的mFirstTouchTarget

通过源码和注释可以知道mFirstTouchTarget 是ViewGroup事件接受链条上的第一个接受者,ViewGroup的事件如果再往下传,肯定会传给这个TouchTarget,通过反射修改这个字段就可以把ViewGroup的事件转发给任何View(更简单直观,通用,但是需要用到反射)。
在这里插入图片描述
如何修改呢,这种肯定只能反射修改了,当然有人会说新版本Android不能反射了,其实大牛们已经有解决方案了,我在这个Demo项目中也用上了,可以参考Demo中的方案,也就一句话解决。另外看这个TouchTarget的源码

在这里插入图片描述
首先它是ViewGroup的私有内部类只能通过反射加载,再调用他的Obtain方法把需要接受这个ViewGroup的事件的字VIew和ponterIdBits 传进去就可以了。pointerIdBits一般传1就可以( 实操传1就可以),构建出TouchTarget后,当你需要改变ViewGroup的默认的事件传递时,反射修改mFirstTouchTarget为你构建的TouchTarget对象即可,之后的事件就会转发给目标View(注意每次按下其实都会重置状态然后新寻找mFirstTouchTarget,所以这种应用更适合处理Down事件之后的事件转发)。

如何修改mFirstTouchTarget代码如下

    /**
     * 把group的首个事件接受者修改为target,达到按需设置事件的接受者
     * (因為需要反射,所以需要先解決反射问题:https://github.com/OBaKai/JJReflection)
     * @param target
     * @param group
     */
    public static void giveFistTargetForViewGroup(View target, ViewGroup group) {
    
    
        try {
    
    
            Field firstTarget = ViewGroup.class.getDeclaredField("mFirstTouchTarget");
            firstTarget.setAccessible(true);
//            Object firstTouchtarget=firstTarget.get(group);
            Class innerClass = Class.forName("android.view.ViewGroup$TouchTarget");
            //obtain(@NonNull View child, int pointerIdBits)
            Method obtainMethod = innerClass.getMethod("obtain", View.class, int.class);
            obtainMethod.setAccessible(true);
            Object newTart=obtainMethod.invoke(null,target,1);
            firstTarget.set(group,newTart);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }


    }

本Demo中在长按RecylerView的Item(Down事件后)后生成了RecylerView的Item截图,然后把后续的事件转发到顶层的触摸控制View上,顺利的完成事件转发,从而完成拖拽过程的实现。

猜你喜欢

转载自blog.csdn.net/wang382758656/article/details/127214888
今日推荐