先看效果
再上源码
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上,顺利的完成事件转发,从而完成拖拽过程的实现。