大白话描述Android事件分发 -简洁版

前言

事件分发机制是Android十分重要的一个机制,在日常开发过程中,我们时常需要利用它的一些特性去完成我们的需求。今天笔者就用比较通俗的话来讲一下,笔者对于事件分发机制的认识和理解。事件分发系列,笔者准备写两篇,一篇基础版,一篇进阶版,在基础版中,笔者只会讲事件分发的基本流程,以及处理方式,而在进阶版中,笔者准备对一些必要的知识进行展开。本篇文章都是默认点击事件发生在Activity中,特殊说明除外。

什么时候会发生事件分发、它的源头是哪?

当我们点击屏幕之后,实际上应用出现的任何反应或者没有反应都是事件分发的结果。所以事件分发发生在我们点击或者说触摸屏幕之后。宏观上讲,桌面也是一个页面,我们点击任何一个图标,都会产生一个触摸事件(MotionEvent)。那触摸事件是从哪来的呢?这个就说来话长了,那咱们就长话短说:实际上在我们触摸屏幕后会产生一个电信号,然后由InputMannageServer通过WindowManagerServer查询我们的点击发生的Window,然后将信息传递给我们的window,然后window会将该信息又交给它所在控件树的“管理者” ViewRootIMPL,在这里我们的管理者为了便于后续控件能够更好的接收该消息,将其打包生成了MotionEvent对象。那么事件分发真正开始的源头,笔者认为是在ViewRootImpl。接下来,我们就从这里出发,一起领略一下事件分发机制。

从viewRootImpl到控件这中间都经历了什么?

ViewRootImpl封装好MotionEvent之后就会将它交给它所管理View树的根。那么Activity中,view树的根会是DecorView,而DecorView是继承自FrameLayout的,也就是说它是一个ViewGroup,对于ViewGroup来说,它会优先判断是否拦截该事件,如果不拦截那么就会交给它的子控件去处理。但是DecorView是一个例外,它的分发方法是被重写了的,所以它并不会去判断是否拦截,而是查看在该分发方法中是否有window的回调方法(Activity实现和Dialog实现)如果不存在则调用父类的分发方法,如果存在则将该MotionEvent交给实现者,这里笔者指明为Activity。当事件到达Activity后,如果事件不是一定需要它来处理,Activity也不会处理(毕竟是一个大忙人)那么它会将该事件继续交给它的助理 - PhoneWindow,phoneWindow内部维护了一个mDecor(Decorview)和一些window的数据,那么事件会继续传递到mDecor这来,到达mDecor后它会调用自身的surperDispatchTouchEvent分发来调用它的父类方法,实际上就是回到了最开始的分发分发中调用父类分发哪,那么大家肯定会疑惑,既然最终又回到了它父类的分发方法,那么为什么要多此一举在这中间经过Activity呢?这是因为如果该事件所有的控件都不处理或者说处理不了,最终该事件是会交给Activity来处理的。如果不做这一层,那么可能会导致事件流失(当然这是笔者读完源码后的想法,如果不对请指出)。

在这里插入图片描述

事件如何从ViewGroup到达具体控件?

从上面我们知道,事件又被传回到了ViewGroup,那么后续会怎么处理呢?磨刀不误砍柴功,我们先来介绍一下整个事件分发中最常出现的几个方法:

dispatchTouchEvent :该方法就是笔者在上述中提到的分发方法,该方法的主要作用就是将事件传递到当前控件,若不处理则再调用子控件的该方法将事件传递到子控件(ViewGroup和View的方法体是不一样的,ViewGroup才会传递到下一个子控件,而View则不会,如果不处理直接返回false)。

onInterceptTouchEvent :该方法是上面提到的判断是否拦截的方法,作用也就是拦截当前事件,给当前控件处理,或者不处理。如果子控件不允许拦截可以调用requestDisallowInterupt方法。拦截返回true,不拦截返回false。

onTouchEvent :在ViewGroup中,若事件拦截后,会将事件交由该方法处理,处理完成返回true,若不处理则返回false。对于View来说mOnTouchListener.onTouch(this, event)会发生在onTouchEvent
之前,如果没有设置监听器,或者没有处理才会交由onTouchEvent处理。

然后我们在介绍一下事件的分类 :

触摸事件的基本类型有三种 :

ACTION_DOWN : 对应按下动作。

ACTION_MOVE : 对于移动动作。

ACTION_UP : 对于抬起手指的动作。

那么一个正确完整的事件应该是从down到up的,但是如果中间产生了一些问题,比如被拦截,那么就会产生一个Cancel事件。

ACTION_CANCEL : 出现异常情况产生的事件。通常事件被中断。

那么其实还存在着一些事件,是由多点触控产生的,这里暂时不做介绍。并且接下来的描述,笔者都默认是down事件,因为在事件分发中存在着一个机制,该机制确保如果正确执行,则接下来的事件都将由接收down事件的控件进行处理,也就是直接将事件进行派发。这里涉及到的东西会在进阶文章中讲到。

那么一个事件是如何到达具体控件的呢?

首先我们假设控件树的最外层是ViewGroup,那么首先我们会调用ViewGroup的dispatchTouchEvent分发,在该方法中,我们会判断子控件是否允许父布局进行拦截,如果不允许则直接调用子控件的dispatchTouchEvent方法进行分发(实际上该方法内部还做了一些处理,这里不做解释),如果允许的话,那么会先调用onInterceptTouchEvent方法进行拦截判断,如果拦截则会返回true,然后下一步则会调用该布局容器的onTouchEvent方法进行处理,处理完成则返回True,否则返回false交由上层处理。如果拦截返回false,则表示不拦截,交由子控件处理,同样会调用子控件的dispatchTouchEvent方法进行分发,那么进一步假设该子控件为View类型,那么在View的dispatchTouchEvent方法中,我们不再进行拦截判断,而是查看是否具有OnTouchListener.onTouch()监听器,如有则将事件交由其处理。否则交由onTouchEvent方法处理。如果子控件消费了该事件,那么返回true,否则返回false。由上层来进行处理。(提前提一句,如果该控件接收了该事件,那么该事件关联的后续事件都将由该控件处理。这是事件分发的一个原则。)

在这里插入图片描述

自此事件从ViewRootImpl到具体控件的大概流程我们就讲完了。该篇文章实际上是让大家对于事件分发有一个大概的框架。然后后续的进阶版就是让大家往框架中填入细节。

如果大家有条件且有兴趣可以去看源码 推荐官方提供的源码查看网站,可以跳转 非常nice

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值