还是不能偏题,其实这样的一个面试问题,确实是一个较为普遍的问题,我相信同类型的文章,网上一搜也是比比皆是,而且简单看一下关注度就能知道有多少人倒在了这种源码类型的面试上。
一般情况下,事件列都是从用户按下(ACTION_DOWN)的那一刻产生的,不得不提到,三个非常重要的与事件相关的方法。
- dispatchTouchEvent()
- onTouchEvent()
- onInterceptTouchEvent()
Activity 的事件分发机制
从英文单词中已经很明显的知道,dispatchTouchEvent()
是负责事件分发的。当点击事件产生后,事件首先会传递给当前的 Activity,这会调用 Activity 的 dispatchTouchEvent()
方法,我们来看看源码中是怎么处理的。
注意截图中,我增加了一些注释,便于我们更加方便的理解,由于我们一般产生点击事件都是 MotionEvent.ACTION_DOWN
,所以一般都会调用到 onUserInteraction()
这个方法。我们不妨来看看都做了什么。
很遗憾,这个方法实现是空的,不过我们可以从注释和其他途径可以了解到,该方法主要的作用是实现屏保功能,并且当此 Activity 在栈顶的时候,触屏点击 Home、Back、Recent 键等都会触发这个方法。
再来看看第二个 if 语句,getWindow().superDispatchTouchEvent()
,getWindow()
明显是获取 Window
,由于 Window
是一个抽象类,所以我们能拿到其子类 PhoneWindow
,我们直接看看 PhoneWindows.superDispatchTouchEvent()
到底做了什么操作。
直接调用了 DecorView
的 superDispatchTrackballEvent()
方法。DecorView
继承于 FrameLayout
,作为顶层 View,是所有界面的父类。而 FrameLayout
作为 ViewGroup
的子类,所以直接调用了 ViewGroup
的 dispatchTouchEvent()
。
ViewGroup 的事件分发机制
我们通过查看 ViewGroup
的 dispatchTouchEvent()
可以发现。
注意其中红框里面的代码,看注释也能知道,定义了一个 boolean 值变量 intercept
来表示是否要拦截事件。
其中采用到了 onInterceptTouchEvent(ev)
对 intercept
进行赋值。大多数情况下,onInterceptTouchEvent()
返回值为 false,但我们完全可以通过重写 onInterceptTouchEvent(ev)
来改变它的返回值,不妨继续往下看,我们后面对这个 intercept
做了什么处理。
暂时忽略 判断的 canceled
,该值同样大多数时候都返回 false,所以当我们没有重写 onInterceptTouchEvent()
并使它的返回值为 true 时,一般情况下都是可以进入到该方法的。
继续阅读源码可以发现,里面做了一个 For 循环,通过倒序遍历 ViewGroup
下面的所有子 View,然后一个一个判断点击位置是否是该子 View 的布局区域,当然还有一些其他的,由于篇幅原因,这里就不细讲了。
View 的事件分发机制
ViewGroup
说到底还是一个 View,所以我们不得不继续看看 View 的 dispatchTouchEvent()
。
截图中的代码是有删减的,我们重点看看没有删减的代码。
红框中的三个条件,第一个我就不用说了。
-
(mViewFlags & ENABLED_MASK) == ENABLED
该条件是判断当前点击的控件是否为 enable,但由于基本 View 都是 enable 的,所以这个条件基本都返回 true。 -
mOnTouchListener.onTouch(this, event)
即我们调用setOnTouchListener()
时必须覆盖的方法onTouch()
的返回值。
从上述的分析,终于知道「onTouch()
方法优先级高于 onTouchEvent(event)
方法」是怎么来的了吧。
再来看看 onTouchEvent()
从上面的代码可以明显地看到,只要 View 的 CLICKABLE 和 LONG_CLICKABLE 有一个为 true,那么 onTouchEvent()
就会返回 true 消耗这个事件。CLICKABLE 和 LONG_CLICKABLE 代表 View 可以被点击和长按点击,我们通常都会采用 setOnClickListener()
和 setOnLongClickListener()
做设置。接着在 ACTION_UP 事件中会调用 performClick()
方法,我们看看都做了什么。
从截图中可以看到,如果 mOnClickListener
不为空,那么它的 onClick()
方法就会调用。
总结
本来写到这就结束了,但回顾一遍还是打算给大家稍微总结一下。
需要总结的小点:
1、Android 事件分发总是遵循 Activity => ViewGroup => View 的传递顺序;
2、onTouch()
执行总优先于onClick()
原本想用文字总结的,结果发现简书上还有这样一篇神文:Android事件分发机制详解:史上最全面、最易懂,所以直接引用一下其中的图片。
- Activity 的事件分发示意图
- ViewGroup 事件分发示意图
- View 的事件分发示意图
- 事件分发工作流程总结
最后
最后
我的面试经验分享可能不会去罗列太多的具体题目,因为我依然认为面试经验中最宝贵的不是那一个个具体的题目或者具体的答案,而是结束面试时,那一刻你的感受以及多天之后你的回味~
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家
在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
【算法合集】
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
roid中高级、架构师对你更是如鱼得水,赶快领取吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!