如需转载请评论或简信,并注明出处,未经允许不得转载

目录

前言
网上说View的事件分发流程的文章非常多,有些文章也写的非常好也非常细致。为什么我还要写一篇这样的文章呢?因为事件分发流程涉及到的方法还是有点多,而且从View和ViewGroup之间来来回回有点绕,所以对于初学者来说看起来可能会有点吃力,所以本文就以相对轻松愉快的方式来介绍一下View的事件分发流程
责任链
View的分发流程呢,其实用到了责任链模式,顾名思义
责任链就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会处理请求,如果某个节点处理完了就可以返回处理完毕或者传递给下一个节点继续处理
看到这个描述,是不是很像我们平时的工作,所以接下来就通过工作中的例子来还原View的事件分发流程
需求:触摸事件
一个公司要做一个好的产品,往往需要察觉市场的需求,所以一个触摸事件就等于是一个市场需求
老板:Activity
老板发现一个市场需求后,他有两个选择
-
完全自己来做:老板认为自己能完成这个需求(重写
Activity#dispatchTouchEvent
,不调用super.dispatchTouchEvent(event)
)

-
吩咐让下面去做:老板往往有很多更重要的事情,所以他就把这个需求交给了(
ViewGroup#dispatchTouchEvent
)技术主管(ViewGroup
)去做。当然,他也可以在下达任务之前简单做一点前置工作(Activtiy#onUserInteraction
)
技术主管:ViewGroup
技术主管接到任务后,他也有两个选择
-
把需求接下来,自己做:(
ViewGroup#onInterceptTouchEvent
返回true),这里还有两种情况
- 主管虽然把需求接下来了,但是自己不会做(
ViewGroup#onTouchEvent
返回false),没办法又重新把活推回给了老板(Activity#onTouchEvent
)

- 主管自己完成了需求:(手动重写
ViewGroup#onTouchEvent
返回true,或者只要这个ViewGroup
的clickable == true
,那么ViewGroup#onTouchEvent
就返回true)

-
把需求分配给下面去做:主管自己不想做这个事情(
ViewGroup#onInterceptTouchEvent
返回false),所以他就把这个事情交给了(View#dispatchTouchEvent
)开发人员(View
)去做
开发人员:View
- 开发人员无法完成这个需求:(
View#onTouchEvent
返回false),所以又会重新把任务推回给主管(ViewGroup
),主管又把任务推回给老板(ViewGroup
)

- 开发人员完成需求:(
View#onTouchEvent
返回true)

通过上面整个过程我们可以看到:
- 事件的分发是通过
dispatchTouchEvent
方法,事件的拦截是通过onInterceptTouchEvent
方法,拦截后对事件的处理是通过onTouchEvent
方法(注意:如果触摸的位置上存在ViewGroup的子View,一定要拦截后才能自己处理,如果触摸的位置上没有ViewGroup的子View,不用拦截即可自己处理) - 事件分发(
dispatchTouchEvent
)的方向是Activity—>ViewGroup—>View,事件处理(onTouchEvent
)的方向是View—>ViewGroup—>Activity - 一旦
onInterceptTouchEvent
返回true,后续的ACTION_UP事件就不会向ViewGroup
传递(主要原因是拦截后会设置一个标识,通过判断这个标识,决定后续事件还要不要传给ViewGroup
处理)
onTouch和onClick
有的人应该已经发现,上面这个过程我们漏掉了我们最熟悉的onTouch
和onClick
方法,为什么把这两个方法分开来说呢,因为我认为上面的过程才是更纯粹的事件分发过程。而onTouch
和onClick
两个方法更像是Android系统为了方便应用层开发,给我们在onTouchEvent
前后提供的两个回调,因为我们并不会总是去继承一个View
重写他的onTouchEvent
方法。比如我们在xml中放置了一个Button
,我们最常见的做法是给它设置监听事件来处理点击,而不是重写onTouchEvent
方法来处理
onTouch:在执行onTouchEvent
前给我们提供的回调,可以让我们在应用层控制这个事件是否需要向onTouchEvent
进行传递,默认返回false。如果返回true,则事件不会交给onTouchEvent
处理,如下图所示

onClick:在View#onTouchEvent
中回调,是系统给我们提供的一种默认的事件处理方式,为了防止被多次调用,会在最终ACTION_UP事件分发到View的时候才会被调用,且只要View的clickable == true
,onTouchEvent
最终就会返回true,代表已经处理事件
总结
-
首先从
Activity
的dispatchTouchEvent
开始,向ViewGroup
传递 -
ViewGroup
可以选择拦截后自己处理,也可以选择继续向下传递。如果当前区域不存在正在被点击的View
,则事件会重写交回给ViewGroup
处理,ViewGroup
如果无法处理会重新交回给Activity
处理。如果当前区域存在正在被点击的View
,那么会将事件交给这个View
处理 -
如果
View
的onTouch
返回true那么事件被消费,如果返回false,最终View
会在接收到ACTION_UP
事件后,执行View
的onClick
,最终onTouchEvent
返回true代表事件被处理完成
网友评论