Android⾯试题精选:讲⼀讲Android的事件分发机制
⾯试场景
讲讲 Android 的事件分发机制?
基本会遵从 Activity => ViewGroup => View 的顺序进⾏事件分发,然后通过调⽤ onTouchEvent() ⽅法进⾏事件的处理。我们在项⽬中⼀般会对 MotionEvent.ACTION_DOWN,MotionEvent.ACTION_UP,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_CANCEL 分情况进⾏操作。
有去查看源码中的事件拦截⽅法吗?或者说在进⾏事件分发的时候如何让正常的分发⽅式进⾏拦截?
我知道有个拦截事件的⽅法叫...叫,onInterceptEvent()?应该是,不过由于平时项⽬较多,确实没时间去关注太多源码。
厄,那你觉得在⼀个列表中,同时对⽗ View 和⼦ View 设置点击⽅法,优先响应哪个?为什么会这样?
肯定是优先响应⼦ View 的,⾄于为什么这样,平时知道这个结论,所以没去太深⼊研究,但我相信我简单看⼀下源码是肯定知道的。
先发表点扯淡
我们可能经常会遇到上⾯的这种情况,⾯试官希望了解我们知识的深⼊情况,或者说是平时学习欲望到底怎样。可很不幸的是,我搞 模拟⾯试 以来,80% 的⼩伙伴都属于开发能⼒不错,可对类似事件分发这样的基础问题⼀概不知。究其原因,除去忙以外,⼤多数⼩伙伴还是觉得平时开发也⽤不上什么,即使⽤到了,直接 Google ⼀下便能得到正确答案。
这⼤概就是很多⼈不会⾃定义 View 的原因吧,⼤多数效果在 GitHub 上都是现成的了,即使不太⼀样,也可以简单改改完事。
可很遗憾的是,我模拟⾯试那额外的 20% 的⼈,总拿到了令⼤多数⼈羡慕嫉妒恨的 offer,这不是没有原因的。可能别⼈就平时的开发中保持了更多的⼀点求知欲,就学到了很多⾄关重要的细节知识。
这⾥是关于的Android 学习,⾯试⽂档,视频收集⼤整理,有兴趣的伙伴们可以看看~
正⽂
还是不能偏题,其实这样的⼀个⾯试问题,确实是⼀个较为普遍的问题,我相信同类型的⽂章,⽹上⼀搜也是⽐⽐皆是,⽽且简单看⼀下关注度就能知道有多少⼈倒在了这种源码类型的⾯试上。
⼀般情况下,事件列都是从⽤户按下(ACTION_DOWN)的那⼀刻产⽣的,不得不提到,三个⾮常重要的与事件相关的⽅法。
dispatchTouchEvent()
onTouchEvent()
onInterceptTouchEvent()
Activity 的事件分发机制
从英⽂单词中已经很明显的知道,dispatchTouchEvent() 是负责事件分发的。当点击事件产⽣后,事件⾸先会传递给当前的 Activity,这会调⽤ Activity 的 dispatchTouchEvent() ⽅法,我们来看看源码中是怎么处理的。
注意截图中,我增加了⼀些注释,便于我们更加⽅便的理解,由于我们⼀般产⽣点击事件都是 Motion
Event.ACTION_DOWN,所以⼀般都会调⽤到 onUserInteraction() 这个⽅法。我们不妨来看看都做了什么。
android模拟点击很遗憾,这个⽅法实现是空的,不过我们可以从注释和其他途径可以了解到,该⽅法主要的作⽤是实现屏保功能,并且当此 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。
即我们调⽤ setOnTouchListener() 时必须覆盖的⽅法 onTouch() 的返回值。
从上述的分析,终于知道「onTouch() ⽅法优先级⾼于 onTouchEvent(event) ⽅法」是怎么来的了吧。
再来看看 onTouchEvent()

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。