当前位置: 首页 > 面试经验 >

安卓面试题(18/30)事件分发机制全解析

优质
小牛编辑
147浏览
2023-03-28

安卓面试题(18/30)事件分发机制全解析





系列专栏:




  • 安卓系统Framework面经专栏链接:Android系统面试题解析大全

  • 安卓系统Framework面经目录详情:Android系统面经_Framework开发面经_150道面试题答案解析





  • 安卓高频面经解析大全专栏链接:Android高频面经解析大全

  • 安卓高频面经解析大全目录详情:安卓面经_Android面经_150道安卓基础面试题目录





  • 嵌入式面经解析大全专栏链接:嵌入式面经_C++软件开发面经_111道面试题全解析

  • 嵌入式面经解析大全目录详情:111道嵌入式面经题全解析软件开发面经C++面经目录




本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;


网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,更有专栏内容免费技术答疑。助您提高安卓面试准备效率,为您面试保驾护航!


正文开始⬇


事件分发机制属于安卓基础面试必问题目之一。最关键是理解事件分发三个函数的作用和返回值的含义,就基本足够应付面试了。面试官可能会问:



  1. 请介绍什么是事件分发机制,以及对应流程⭐⭐⭐⭐⭐

  2. 谈谈你对MotionEvent的认识?Cancel事件是什么情况下触发的⭐⭐⭐

  3. 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]dispatchTransformedTouchEvent():


private boolean dispatchTransformedTouchEvent(


 类似资料: