写点什么

Android 事件分发机制五:面试官你坐啊,android 串口开发入门

用户头像
Android架构
关注
发布于: 刚刚


本文是最后一篇,主要是模拟面试情况提出一些问题以及解答,也当是整个事件分发知识的回顾。读者也可以尝试一下看看这些问题是否都能解答出来。


面试开始




  1. 学过事件分发吗,聊聊什么是事件分发


事件分发是将屏幕触控信息分发给控件树的一个套机制。 当我们触摸屏幕时,会产生一些列的 MotionEvent 事件对象,经过控件树的管理者 ViewRootImpl,调用 view 的 dispatchPointerEvnet 方法进行分发。


  1. 那主要的分发流程是什么:


在程序的主界面情况下,布局的顶层 view 是 DecorView,他会先把事件交给 Activity,Activity 调用 PhoneWindow 的方法进行分发,PhoneWindow 会调用 DecorView 的父类 ViewGroup 的 dispatchTouchEvent 方法进行分发。也就是 Activity->Window->ViewGroup 的流程。ViewGroup 则会向下去寻找合适的控件并把事件分发给他。


  1. 事件一定会经过 Activity 吗?


不是的。我们的程序界面的顶层 viewGroup,也就是 decorView 中注册了 Activity 这个 callBack,所以当程序的主界面接收到事件之后会先交给 Activity。 但是,如果是另外的控件树,如 dialog、popupWindow 等事件流是不会经过 Activity 的。只有自己界面的事件才会经 Activity。


  1. Activity 的分发方法中调用了 onUserInteraction()方法,你能说说这个方法有什么作用吗?


好的。这个方法在 Activity 接收到 down 的时候会被调用,本身是个空方法,需要开发者自己去重写。 通过官方的注释可以知道,这个方法会在我们以任意的方式开始与 Activity 进行交互的时候被调用。比较常见的场景就是屏保:当我们一段时间没有操作会显示一张图片,当我们开始与 Activity 交互的时候可在这个方法中取消屏保;另外还有没有操作自动隐藏工具栏,可以在这个方法中让工具栏重新显示。


  1. 前面你讲到最后会分发到 viewGroup,那么 viewGroup 是如何分发事件


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


的?


viewGroup 处理事件信息分为三个步骤:拦截、寻找子控件、派发事件。



事件分发中有一个重要的规则:一个触控点的一个事件序列只能给一个 view 处理,除非异常情况。所以如果 viewGroup 消费了 down 事件,那么子 view 将无法收到任何事件。



viewGroup 第一步会判读这个事件是否需要分发给子 view,如果是则调用 onInterceptTouchEvent 方法判断是否要进行拦截。 第二步是如果这个事件是 down 事件,那么需要为他寻找一个消费此事件的子控件,如果找到则为他创建一个 TouchTarget。 第三步是派发事件,如果存在 TouchTarget,说明找到了消费事件序列的子 view,直接分发给他。如果没有则交给自己处理。


  1. 你前面讲到“一个触控点的一个事件序列只能给一个 view 处理,除非异常情况”,这里有什么异常情况呢?如果发生异常情况该如何处理?


这里的异常情况主要有两点:1.被 viewGroup 拦截,2.出现界面跳转等其他情况。



当事件流中断时,viewGroup 会发送一个 ACTION_CANCEL 事件给到 view,此时需要做一些状态的恢复工作,如终止动画,恢复 view 大小等等。


  1. 那既然说到 ACTION_CANCEL 类型,那你可以说说还有什么事件类型吗?


除了 ACTION_CANCEL,其他事件类型还有:



  • ACTION_MOVE:当我们手指在屏幕上滑动时产生此事件


  • ACTION_UP:当我们手指抬起时产生此事件



此外多指操作也比较常见:



  • ACTION_POINTER_DOWN: 当已经有一个手指按下的情况下,另一个手指按下会产生该事件


  • ACTION_POINTER_UP: 多个手指同时按下的情况下,抬起其中一个手指会产生该事件。



一个完整的事件序列是从 ACTION_DOWN 开始,到 ACTION_UP 或者 ACTION_CANCEL 结束。 一个手指的完整序列是从 ACTION_DOWN/ACTION_POINTER_DOWN 开始,到 ACTION_UP/ACTION_POINTER_UP/ACTION_CANCEL 结束。


  1. 哦?说到多指,那你知道 ViewGroup 是如何将多个手指产生的事件准确分发给不同的子 view 吗


这个问题的关键在于 MotionEvent 以及 ViewGroup 内部的 TouchTarget。



每个 MotionEvent 中都包含了当前屏幕所有触控点的信息,他的内部用了一个数组来存储不同的触控 id 所对应的坐标数值。



当一个子 view 消费了 down 事件之后,ViewGroup 会为该 view 创建一个 TouchTarget,这个 TouchTarget 就包含了该 view 的实例与触控 id。这里的触控 id 可以是多个,也就是一个 view 可接受多个触控点的事件序列。



当一个 MotionEvent 到来之时,ViewGroup 会将其中的触控点信息拆开,再分别发送给感兴趣的子 view。从而达到精准发送触控点信息的目的。


  1. 那 view 支持处理多指信息吗?


View 默认是不支持的。他在获取触控点信息的时候并没有传入触控点索引,也就是获取的是 MotionEvent 内部数组中的第一个触控点的信息。多指需要我们自己去重写方法支持他。


  1. 嗯嗯...那 View 是如何处理触摸事件的?


首先,他会判断是否存在 onTouchListener,存在则会调用他的 onTouch 方法来处理事件。如果该方法返回 true 那么就分发结束直接返回。而如果该监听器为 null 或者 onTouch 方法返回了 false,则会调用 onTouchEvent 方法来处理事件。



onTouchEvent 方法中支持了两种监听器:onClickListener 和 onLongClickListener。View 会根据不同的触摸情况来调用这两个监听器。同时进入到 onTouchEvent 方法中,无论该 view 是否是 enable,只要是 clickable,他的分发方法都是返回 true。



总结一下就是:先调用 onTouchListener,再调用 onClickListener 和 onLongClickListener。


  1. 你前面多次讲到分发方法和返回值,那你可以讲讲主要有什么方法以及他们之间的关系吗?


嗯嗯。核心的方法有三个:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。



简单来说:dispatchTouchEvent 是核心的分发方法,所有分发逻辑都在这个方法中执行;onInterceptTouchEvent 在 viewGroup 负责判断是否拦截;onTouchEvent 是消费事件的核心方法。viewGroup 中拥有这三个方法,而 view 没有 onInterceptTouchEvent 方法。



  • viewGroup


1.  viewGroup的dispatchTouchEvent方法接收到事件消息,首先会去调用onInterceptTouchEvent判断是否拦截事件


    *   如果拦截,则调用自身的onTouchEvent方法


    *   如果不拦截则调用子view的dispatchTouchEvent方法


2.  子view没有消费事件,那么会调用viewGroup本身的onTouchEvent


3.  上面1、2步的处理结果为viewGroup的dispatchTouchEvent方法的处理结果,没有消费则返回false并返回给上一层的onTouchEvent处理,如果消费则分发结束并返回true。


  • view


1.  view的dispatchTouchEvent默认情况下会调用onTouchEvent来处理事件,返回true表示消费事件,返回false表示没有消费事件


2.  第1步的结果就是dispatchTouchEvent方法的处理结果,成功消费则返回true,没有消费则返回false并交给上一层的onTouchEvent处理



简单来说,在控件树中,每个 viewGroup 在 dispatchTouchEvent 方法中不断往下分发寻找消费的 view,如果底层的 view 没有消费事件则会一层层网上调用 viewGroup 的 onTouchEvent 方法来处理事件。


用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android事件分发机制五:面试官你坐啊,android串口开发入门