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被消费了,所以不会让上层处理了。
尾声
面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Android核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、热修复设计、插件化框架解读、组件化框架设计、图片加载框架、网络、设计模式、设计思想与代码质量优化、程序性能优化、开发效率优化、设计模式、负载均衡、算法、数据结构、高级UI晋升、Framework内核解析、Android组件内核等。
不仅有学习文档,视频+笔记提高学习效率,还能稳固你的知识,形成良好的系统的知识体系。这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《2017-2021字节跳动Android面试历年真题解析》
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
7)]
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-p0ae0afc-1714641422751)]
《2017-2021字节跳动Android面试历年真题解析》
[外链图片转存中…(img-C5WpDVoM-1714641422753)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!