Parameters
ev | The motion event being dispatched down the hierarchy. |
Returns
· Return true to steal motionevents from the children and have them dispatched to this ViewGroup throughonTouchEvent(). The current target will receive an ACTION_CANCEL event, and nofurther messages will be delivered here.
基本意思就是:
1. ACTION_DOWN首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
Android touch事件传递机制:
我们可以看看android源代码:
Activity.java中
暂且不管onUserInteraction方法因为它只是一个空方法如果你没实现的话。getWindow().superDispatchTouchEvent(ev)。其中getWindow()返回的是PhoneWindow。
PhoneWindow.java:
此函数调用super.dispatchTouchEvent(event),Activity的rootview是PhoneWindow.DecorView,它继承FrameLayout。通过super.dispatchTouchEvent把touch事件派发给各个Activity的是子view。同时我可以看到,如果子view拦截了事件,则不会执行onTouchEvent函数。
ViewGroup.java中dispatchTouchEvent方法:
由于代码过长这里就不贴出来了,但也知道它返回的是
return target.dispatchTouchEvent(ev);
这里target指的是所分发的目标,可以是它本身,也可以是它的子View。
ViewGroup.java中的onInterceptTouchEvent方法:
默认情况下返回false。即不拦截touch事件。
View.java中的dispatchTouchEvent方法
这里我们很清楚可以知道如果if条件不成立则dispatchTouchEvent的返回值是onTouchEvent的返回值
View.java中的onTouchEvent方法
所以很容易得到触摸事件默认处理流程(以ACTION_DOWN事件为例):
当触摸事件ACTION_DOWN发生之后,先调用Activity中的dispatchTouchEvent函数进行处理,紧接着ACTION_DOWN事件传递给ViewGroup中的dispatchTouchEvent函数,接着viewGroup中的dispatchTouchEvent中的ACTION_DOWN事件传递到调用ViewGroup中的onInterceptTouchEvent函数,此函数负责拦截ACTION_DOWN事件。由于viewGroup下还包含子View,所以默认返回值为false,即不拦截此ACTION_DOWN事件。如果返回false,则ACTION_DOWN事件继续传递给其子view。由于子view不是viewGroup的控件,所以ACTION_DOWN事件接着传递到onTouchEvent进行处理事件。此时消息的传递基本上结束。从上可以分析,motionEvent事件的传递是采用隧道方式传递。隧道方式,即从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。
接下来继续分析,事件的处理。刚才ACTION_DOWN事件传递到view的onTouchEvent函数中处理了,默认是返回true,接着view的dispatchTouchEvent返回true,再接着viewGroup的dispatchTouchEvent返回true,最后Activity的dispatchTouchEvent返回true。我们发现,motionEvent事件的处理采用冒泡方式。冒泡方式,从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。
下图为程序调试结果:
ACTION_DOWN事件输出:
ACTION_MOVE事件输出:
现在我们来做一些改变,就接着以ACTION_DOWN为例
情况一:
我们在View中onTouchEvent中ACTION_DOWN返回false,输出结果如下:
可以发现ACTION_DOWN事件传递到上层的ViewGroup的onTouchEvent,同时返回true,说明事件被ViewGroup消费了。同时之后的touch事件(ACTION_MOVE等)不再传递给view,只传递到ViewGroup,由ViewGroup的onTouchEvent函数处理touch事件。同时onInterceptTouchEvent也不再调用。
情况二:
我们在View中onTouchEvent中ACTION_MOVE返回false,输出结果如下:
由于view未消费此ACTION_MOVE事件,按照原理来说应该是将事件处理冒泡到ViewGroup去处理,但结果却是Activity处理的。我们知道,触摸事件首先发生的就是ACTION_DOWN事件,我们在onInterceptTouchEvent所解释就可以发现ACTION_DOWN与ACTION_MOVE等事件有区别,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。也就是说ACTION_DOWN事件被view消费了,而ACTION_MOVE事件没被消费,传递到ViewGroup,由于之前ViewGroup没处理ACTION_DOWN事件,所以它也不处理ACTION_MOVE。但Activity却不一样,它可以接受所有事件。
情况三:
这次在ViewGroup中的onInterceptTouchEvent中ACTION_DOWN返回true
结果如下:
它直接把事件发送给ViewGroup的onTouchEvent处理,此后不再拦截事件直接到viewGroup中的onTouchEvent处理。
情况四:
在ViewGroup中的onInterceptTouchEvent中ACTION_MOVE返回true
结果如下:
ACTION_MOVE被ViewGroup拦截了,上次处理ACTION_DOWN的view则会收到ACTION_CANCEL事件,之后ViewGroup不再拦截后续事件,事件直接在ViewGroup中的onTouchEvent处理。
还有很多情况,这里不一一列出了。
在写这个demo一开始,我发现重写了onTouchEvent函数就无法获取onClick和onLongClick事件。接下来讨论当重写了onTouchEvent,android是如何区分onClick,onLongClick事件的。搞清楚此问题对于如何响应UI各种事件是很重要的,例如类似android桌面的应用程序图标,可以点击,然后长按拖动。
Android中onclick,onLongClick是都是由ACTION_DOWN,ACTION_UP组成。如果在同一个View中onTouchEvent、onclick、onLongClick都进行了重写。onTouchEvent最先捕获ACTION_DOWN、ACTION_UP等单元事件。接下来才可能发生onClick、onLongClick事件。一个onclick事件是由ACTION_DOWN和ACTION_UP组成的。一个onLongClick事件至少有一个ACTION_DOWN。那android具体是怎么实现的呢,可以看源代码:
View.java中:
上面我已经展示了onTouchEvent方法,但由于过长我折叠了一部分代码,现在展开
这个if条件内执行就是click事件处理及longClick事件处理。先看ACTION_DOWN事件
我们看到有个postDelayed方法,此方法意思为延时把线程插入到消息队列。即ACTION_DOWN后触发一个postDelayed方法。mPendingCheckForTap属于CheckForTap的实例。
在里面开启一个线程当为LONG_CLICKABLE,调用postCheckForLongClick方法。
再看mPendingCheckForLongPress这个线程。
当上面一系列条件全都符合的情况就调用performLongClick方法。
此方法就调用我们熟悉的onLongClick函数。
至此onLongClick事件已经分析完。再接着看ACTION_UP事件
直接关注performClick函数:
这里我们同样看到了我们熟悉的onClick方法。
所以android这种机制是保证了此onClick和onLongClick能与onTouchEvent并存。接下来考虑onclick与onLongClick是否并存,其实这个问题前面已经阐述了。只要此事件没被消费,它还会接着传递下去。从上面知道onLongClick是在单独的线程执行,发生在ACTION_UP之前。Onclick发生在ACTION_UP之后,也就是说,如果在onLongClick返回false,onclick就会发生,而onlongClick返回true,则代表此事件已经被消费。Onclick不再发生。
返回false:
**
**
返回true
**
**
如果多次设置onclick事件,则最顶层的onclick覆盖掉底层onclick事件;多次设置onLongClick事件,则只执行底层view的onLongClick方法。当ACTION_DOWN调用之后返回false。
可以看到ACTION_DOWN被消费了,所以不会让上层处理了。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
2914)]
[外链图片转存中…(img-0hy5WhWK-1715488552914)]
[外链图片转存中…(img-xPjK151I-1715488552915)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!