安卓面试题(18/30)事件分发机制全解析
牛客高级系列专栏:
安卓(安卓系统开发也要掌握)
- 想通关安卓面试,请看(承诺免费售后答疑):《150道安卓高频面试题目录及答案链接》
- 想通关安卓系统面试,请看:《140道安卓系统Framework面试题目录及答案链接》
- 想进阶安卓开发,请看(承诺免费售后答疑):《Android进阶知识体系解析_15大安卓进阶必备知识点》
- 想了解安卓APP完整开发流程,请看(承诺免费售后答疑):《安卓APP完整开发流程》
- 想掌握安卓App性能优化,请看(承诺免费售后答疑):《安卓性能优化讲解和实战专栏》
- 想掌握Gradle语法和配置,制作Gradle插件,请看(承诺免费售后答疑):《安卓Gradle语法解析和实践大全》
嵌入式
- 想通关嵌入式面试,请看: 《111道嵌入式面试题目录及答案链接》
- 想多掌握几个嵌入式项目,请看:《6个嵌入式项目交流分享(附源码)》
本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;
网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,承诺提供专栏内容免费技术答疑,直接咨询即可。助您提高安卓面试准备效率,为您面试保驾护航!
正文开始⬇
事件分发机制属于安卓基础面试必问题目之一。最关键是理解事件分发三个函数的作用和返回值的含义,就基本足够应付面试了。面试官可能会问:
- 请介绍什么是事件分发机制,以及对应流程⭐⭐⭐⭐⭐
- 谈谈你对MotionEvent的认识?Cancel事件是什么情况下触发的⭐⭐⭐
- OnTouchListener & OnTouchEvent & OnClickListener三者之间的关系⭐⭐⭐⭐
看完以下的解析,一定可以让面试官眼前一亮。
目录
- 1、触摸事件分发流程
- 2、触摸事件分发的3个重要方法
- 3、onTouch()、onTouchEvent()、onClick()傻傻分不清?
- 4、事件分发总结
- 4.1 表格总结
- 4.2 伪代码表示
- 4.3 事件由上到下的传递规则
- 4.4 事件由下而上的传递规则
1、触摸事件分发流程
在《Activity、Window、DecorView以及ViewRoot层级关系全解析》一节有下图:
我们知道View的结构是树形结构,View可以放在ViewGroup中,ViewGroup也可以放在另一个ViewGroup中,如此就形成了层层嵌套的关系。当我们触摸到屏幕后,就会生成一个Touch事件,常见的Touch事件有:
- MotionEvent.ACTION_DOWN:按下
- MotionEvent.ACTION_MOVE:滑动
- MotionEvent.ACTION_CANCEL:非人为原因结束本次事件
- MotionEvent.ACTION_UP:抬起 一般来说,一个事件会经过:按下 --》 滑动 --》抬起,这三个阶段,并在这个过程中会有非人为原因结束本次触摸流程。这些事件会在代码里会封装成一个MotionEvent。那么,当MotionEvent产生后,系统就会将其传递给View树,MotionEvent在View的层级传递,并最终得到处理的过程,就是触摸事件分发流程。一个流程的传递顺序是:
Activity/Window --> ViewGroup --> View
其中,View就是各种控件,如Button、TextView等,而ViewGroup是View的子类,因此本质上也是一个View,只不过ViewGroup可以包含多个子View和定义布局参数。
2、触摸事件分发的3个重要方法
有以下3个重要方法是必须掌握的:
- dispatchTouchEvent(MotionEvent ev):进行事件的分发,在View和ViewGroup类都有该方法,下文会对该方法的源码进行分析,需要区分清楚;
- onInterceptTouchEvent(MotionEvent ev):进行事件拦截,在dispatchTouchEvent()中调用,在分发的过程中判断是否需要进行拦截,需要注意的是只有ViewGroup有该方法,View是没有提供该方法的。如果返回true代表拦截,返回false代表不拦截;
- onTouchEvent(MotionEvent ev):触摸事件处理,同样在dispatchTouchEvent()方法中进行调用,如果返回true代表已处理事件,返回false代表不处理事件,事件继续传递。
为了更好的了解三者的关系,我们从源码出发,首先看看ViewGroup的dispatchTouchEvent(),源码是Android9.0.0=》/frameworks/base/core/java/android/view/ViewGroup.java:
public boolean dispatchTouchEvent(MotionEvent ev) {
...
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev); //1
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
if (newTouchTarget == null && childrenCount != 0) {
...
for (int i = childrenCount - 1; i >= 0; i--) { //2:遍历ViewGroup的子View
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
...
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { //3
...
break;
}
...
}
}
}
}
在[注释1]调用了onInterceptTouchEvent()方法来判断是否要拦截当前的事件。
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
可以看出onInterceptTouchEvent()默认返回false,代表不拦截。接着在[注释2]遍历ViewGroup的子View,如果子View可以接收到触摸事件,则会执行[注释3]dispatc
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
#提供免费售后答疑!!花一杯奶茶的钱获得安卓面试答疑服务,稳赚不赔# Android发展已经很多年,安卓资料网上千千万,本专栏免费提供专栏内容技术答疑!!私聊当天必回。在阅读过程或者其他安卓学习过程有疑问,都非常欢迎私聊交流。