前言
很快乐遇见你~
事件散发系列文章曾经到最初一篇了,先来回顾一下后面四篇,也当个目录:
- Android 事件散发机制一:事件是如何达到 activity 的?: 从 window 机制登程剖析了事件散发的整体流程,以及事件散发的真正终点
- Android 事件散发机制二:viewGroup 与 view 对事件的解决 : 源码剖析了 viewGroup 和 view 是如何散发事件的
- Android 事件散发机制三:事件散发工作流程 : 剖析了触摸事件在控件树中的散发流程模型
- Android 事件散发机制四:学了事件散发有什么用?: 从实战的角度分析事件散发的使用
本文是最初一篇,次要是模仿面试状况提出一些问题以及解答,也当是整个事件散发常识的回顾。读者也能够尝试一下看看这些问题是否都能解答进去。
面试开始
-
学过事件散发吗,聊聊什么是事件散发
事件散发是将屏幕触控信息分发给控件树的一个套机制。
当咱们触摸屏幕时,会产生一些列的 MotionEvent 事件对象,通过控件树的管理者 ViewRootImpl,调用 view 的 dispatchPointerEvnet 办法进行散发。 -
那次要的散发流程是什么:
在程序的主界面状况下,布局的顶层 view 是 DecorView,他会先把事件交给 Activity,Activity 调用 PhoneWindow 的办法进行散发,PhoneWindow 会调用 DecorView 的父类 ViewGroup 的 dispatchTouchEvent 办法进行散发。也就是 Activity->Window->ViewGroup 的流程。ViewGroup 则会向上来寻找适合的控件并把事件分发给他。
-
事件肯定会通过 Activity 吗?
不是的。咱们的程序界面的顶层 viewGroup,也就是 decorView 中注册了 Activity 这个 callBack,所以当程序的主界面接管到事件之后会先交给 Activity。
然而,如果是另外的控件树,如 dialog、popupWindow 等事件流是不会通过 Activity 的。只有本人界面的事件才会经 Activity。 -
Activity 的散发办法中调用了 onUserInteraction()办法,你能说说这个办法有什么作用吗?
好的。这个办法在 Activity 接管到 down 的时候会被调用,自身是个空办法,须要开发者本人去重写。
通过官网的正文能够晓得,这个办法会在咱们以任意的形式 开始 与 Activity 进行交互的时候被调用。比拟常见的场景就是屏保:当咱们一段时间没有操作会显示一张图片,当咱们开始与 Activity 交互的时候可在这个办法中勾销屏保;另外还有没有操作自动隐藏工具栏,能够在这个办法中让工具栏从新显示。 -
后面你讲到最初会散发到 viewGroup,那么 viewGroup 是如何散发事件的?
viewGroup 处理事件信息分为三个步骤:拦挡、寻找子控件、派发事件。
事件散发中有一个重要的规定:一个触控点的一个事件序列只能给一个 view 解决,除非异常情况。所以如果 viewGroup 生产了 down 事件,那么子 view 将无奈收到任何事件。
viewGroup 第一步会判读这个事件是否须要分发给子 view,如果是则调用 onInterceptTouchEvent 办法判断是否要进行拦挡。
第二步是如果这个事件是 down 事件,那么须要为他寻找一个生产此事件的子控件,如果找到则为他创立一个 TouchTarget。
第三步是派发事件,如果存在 TouchTarget,阐明找到了生产事件序列的子 view,间接分发给他。如果没有则交给本人解决。 -
你后面讲到“一个触控点的一个事件序列只能给一个 view 解决,除非异常情况”, 这里有什么异常情况呢?如果产生异常情况该如何解决?
这里的异常情况次要有两点:1. 被 viewGroup 拦挡,2. 呈现界面跳转等其余状况。
当事件流中断时,viewGroup 会发送一个 ACTION_CANCEL 事件给到 view,此时须要做一些状态的复原工作,如终止动画,复原 view 大小等等。
-
那既然说到 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 完结。 -
哦?说到多指,那你晓得 ViewGroup 是如何将多个手指产生的事件精确分发给不同的子 view 吗
这个问题的关键在于 MotionEvent 以及 ViewGroup 外部的 TouchTarget。
每个 MotionEvent 中都蕴含了以后屏幕所有触控点的信息,他的外部用了一个数组来存储不同的触控 id 所对应的坐标数值。
当一个子 view 生产了 down 事件之后,ViewGroup 会为该 view 创立一个 TouchTarget,这个 TouchTarget 就蕴含了该 view 的实例与触控 id。这里的触控 id 能够是多个,也就是一个 view 可承受多个触控点的事件序列。
当一个 MotionEvent 到来之时,ViewGroup 会将其中的触控点信息拆开,再别离发送给感兴趣的子 view。从而达到精准发送触控点信息的目标。
-
那 view 反对解决多指信息吗?
View 默认是不反对的。他在获取触控点信息的时候并没有传入触控点索引,也就是获取的是 MotionEvent 外部数组中的第一个触控点的信息。多指须要咱们本人去重写办法反对他。
-
嗯嗯 … 那 View 是如何解决触摸事件的?
首先,他会判断是否存在 onTouchListener,存在则会调用他的 onTouch 办法来处理事件。如果该办法返回 true 那么就散发完结间接返回。而如果该监听器为 null 或者 onTouch 办法返回了 false,则会调用 onTouchEvent 办法来处理事件。
onTouchEvent 办法中反对了两种监听器:onClickListener 和 onLongClickListener。View 会依据不同的触摸状况来调用这两个监听器。同时进入到 onTouchEvent 办法中,无论该 view 是否是 enable,只有是 clickable,他的散发办法都是返回 true。
总结一下就是:先调用 onTouchListener,再调用 onClickListener 和 onLongClickListener。
-
你后面屡次讲到散发办法和返回值,那你能够讲讲次要有什么办法以及他们之间的关系吗?
嗯嗯。外围的办法有三个:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。
简略来说:dispatchTouchEvent 是外围的散发办法,所有散发逻辑都在这个办法中执行;onInterceptTouchEvent 在 viewGroup 负责判断是否拦挡;onTouchEvent 是生产事件的外围办法。viewGroup 中领有这三个办法,而 view 没有 onInterceptTouchEvent 办法。
-
viewGroup
-
viewGroup 的 dispatchTouchEvent 办法接管到事件音讯,首先会去调用 onInterceptTouchEvent 判断是否拦挡事件
- 如果拦挡,则调用本身的 onTouchEvent 办法
- 如果不拦挡则调用子 view 的 dispatchTouchEvent 办法
- 子 view 没有生产事件,那么会调用 viewGroup 自身的 onTouchEvent
- 下面 1、2 步的处理结果为 viewGroup 的 dispatchTouchEvent 办法的处理结果,没有生产则返回 false 并返回给上一层的 onTouchEvent 解决,如果生产则散发完结并返回 true。
-
-
view
- view 的 dispatchTouchEvent 默认状况下会调用 onTouchEvent 来处理事件,返回 true 示意生产事件,返回 false 示意没有生产事件
- 第 1 步的后果就是 dispatchTouchEvent 办法的处理结果,胜利生产则返回 true,没有生产则返回 false 并交给上一层的 onTouchEvent 解决
简略来说,在控件树中,每个 viewGroup 在 dispatchTouchEvent 办法中一直往下散发寻找生产的 view,如果底层的 view 没有生产事件则会一层层网上调用 viewGroup 的 onTouchEvent 办法来处理事件。
同时,因为 Activity 继承了 Window.CallBack 接口,所以也有 dispatchTouchEvent 和 onTouchEvent 办法:
- activity 接管到触摸事件之后,会间接把触摸事件分发给 viewGroup
- 如果 viewGroup 的 dispatchTouchEvent 办法返回 false,那么会调用 Activity 的 onTouchEvent 来处理事件
- 第 1、2 步的处理结果就是 activity 的 dispatchTouchEvent 办法的处理结果,并返回给下层
-
-
看来你对事件散发理解得挺多的,那你在理论中有使用到事件散发吗?
嗯嗯,有的。举两个例子。
第一个需要是要设计一个按钮块,按下的时候会放大高度变低同时变得半透明,放开的时候又会回弹。这个时候就能够在这个按钮的 onTouchEvent 办法中判断事件类型:down 则开启按下动画,up 则开启开释动画。同时留神接管到 cancel 事件的时候要复原状态。
第二个是滑动抵触。解决滑动抵触的外围思路就是把滑动事件依据具体的状况分发给 viewGroup 或者外部 view。次要的办法有内部拦截法和外部拦截法。
内部拦截法的思路就是在 viewGroup 中判断滑动的状况,对合乎本身滑动的事件进行拦挡,对不合乎的事件不拦挡,给到外部 view。外部拦截法的思路要求 viewGroup 拦挡除了 down 事件以外的所有事件,而后再外部 view 中判断滑动的状况,对合乎本身滑动状况的工夫设置禁止拦挡标记,对不合乎本身滑动状况的事件则勾销标记让 viewGroup 进行拦挡。 -
那内部和外部拦截法该如何抉择呢?
在个别的状况下,内部拦截法不须要对子 view 进行办法重写,比外部拦截法更加简略,举荐应用内部拦截法。
但如果须要在子 view 判断更多的触摸状况时,则应用外部拦截法可更加办法子 view 解决状况。
-
后面始终聊到触摸事件,那你晓得一个触摸事件是如何从触摸屏幕开始产生的吗?
额 …. 在屏幕接管到触摸信息后,会把这个信息交给 InputServiceManager 去解决,最初通过 WindowManagerService 找到合乎的 window,并把触摸信息发送给 viewRootImpl,viewRootImpl 通过层层封和解决之后,产生一个 MotionEvent 事件分发给 view。
-
能够具体讲讲后面 IMS 解决的流程吗?
啊。。这。。。嗯。。。。不会。。。
-
你还有什么想问的吗?
可不可以。。。。给我个小小的点赞再走?
-
下次肯定。
=_=….
最初
对于面试,我始终保持的一个观点就是:能够面向面试知识点学习,但不可面向面试题目答案学习。把相干热门题目的答案背诵下来能够忽悠到一些面试官,但当初基本上都不是简略的询问什么是事件散发,而会给一个具体的需要让咱们思考等等。背诵面试题短期可能会让咱们如同学到了很多,但事实上,咱们什么都没学到。
事件散发系列文章到此完结。有疑难欢送评论区交换,心愿文章对你有帮忙~
都看到这了,要不给作者留下个点赞再走(:
全文到此,原创不易,感觉有帮忙能够点赞珍藏评论转发。
笔者满腹经纶,有任何想法欢送评论区交换斧正。
如需转载请评论区或私信交换。另外欢迎光临笔者的集体博客:传送门