目录
一、发现问题
最近在利用RecyclerView做开发的时候,遇到了一点问题:
给RecyclerView的子项添加事件监听的时候,发现ACITON_DOWN
能得到处理,ACITON_UP
和ACTION_MOVE
却得不到处理。
二、原因分析
在刚开始开发需求的时候我还不太了解事件分发的机制。所以我先去学习了一下事件分发,这里对事件分发做一个简单的总结。
1、事件分发的机制
事件分发是由三个方法配合完成的:
-
dispatchTouchEvent() 分发事件
-
onInterceptTouchEvent() 拦截事件
-
onTouchEvent() 处理事件
而且事件分发的顺序是:
Activity -> ViewGroup -> View
借助一张图来配合理解:
(图源:Android事件分发机制详解:史上最全面、最易懂 - 天涯海角路 - 博客园 (cnblogs.com))
通过图片我们可以看到,ViewGroup是比较特殊的。onInterceptTouchEven()
是他独有方法,他可以将事件拦截下来选择不分发给下一层的View而是自己处理。
2、原因猜测
在了解了事件分发的机制过后,我就猜测会不会是因为RecyclerView将事件拦截了下来。因为RecyclerView肯定有他自己的事件监听,当ACTION_MOVE
的时候应该会触发滚动,加载数据然后显示到屏幕上。
那如果真的是被RecyclerView给拦截了,那我又产生了新的疑问:
-
根据事件分发的机制,再
ACITON_DOWN
的时候应该就决定了targetView是itemView,为什么在ACITON_MOVE
的时候会目标View又变成了RecyclerView? -
RecyclerView是怎么做到只拦截
ACTION_MOVE
和ACTION_UP
而不拦截ACTION_DOWN
的呢? -
那如果想要实现子项自己处理
ACTION_MOVE
和ACTION_UP
要怎么处理呢?
为了验证我的猜想和解决这些疑问,我决定去RecyclerView的源码里一探究竟。
三、RecyclerView事件拦截机制
既然我们要分析的是拦截机制,那么当然应该去onTouchEvent()
这个方法里去看。
这里先说明一下,以下贴出来的源码并不是全部。我一直觉得分析源码不能一行一行的扣,不然思路会很混乱。在这篇文章里,我只把对解决问题有用的部分贴了出来,也能让大家更好理解。如果有小伙伴有看不懂的地方,可以再配合所有源码来理解。
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
...
mInterceptingOnItemTouchListener = null;
if (findInterceptingOnItemTouchListener(e)) {
cancelScroll();
return true;
}
...
final int action = e.getActionMasked();
...
switch (action) {
case MotionEvent.ACTION_DOWN: {
...
} break
...
case MotionEvent.ACTION_MOVE: {
...
} break;
...
case MotionEvent.ACTION_UP: {
..
} break;
...
}
return mScrollState == SCROLL_STATE_DRGING;
}
总的来说这个函数我把他分为两个部分,switch(aciton)之前和switch(aciton)部分。我们按倒序分析一下。
switch (action)部分
返回值
这部分呢我们需要先看一下最后的返回值,因为返回值决定了是否拦截。