Android 事件分发,说白了就是一个责任链模式

284 阅读4分钟

一道简单的面试题面试题?

ViewGroup 和 ChildView 同时监听( View.onClick()),哪个会执行?

看完这篇文章一定会理解,如果还不理解请留言

一、责任链模式优点

一提起责任链模式你是不是马上想起OkHttp的拦截器?

用拦截器的思想思考 View 的 事件分发其实整个思路就清晰了。

责任链模式让这个流程非常简洁。理解也非常容易。

二、Android 事件分发机制

0.事件是如何产生的?

大部分的图形界面其实都会使用树形结构,这样层级会比较清晰。逻辑也很简单。

这里要着重说明一个非常重要的点,也是很多开发者容易被忽略的点。事件是如何产生的?理解了这一点,整个流程就非常好理解了。

首先,我们的点击事件,滑动事件,其实都是从硬件传递过来的。这里会涉及硬件,驱动,操作系统等一系列内容,这个不在这里探讨。我们只要知道,系统会把各种操作事件,发送到当前屏幕正在显示的 Activity,这里一定要理解,整个View事件分发的起始位置一定是从Activity开始的,理解了这一点,后面分析事件分发就比较容易了。

事件触发之后传递到 Activity, 然后通过 Activity 通过PhoneWindow 传递到DecorView。

Activity PhoneWindow DecorView 为整个View的跟, 之间的关系可以简单理解为 DecorView 为整个View 树的的跟View, PhoneWindow 就是连接Activity 和 DecorView 的桥梁,这个内容也不是本文展开的重点。

有了跟View 那么整个View 树就可以一点点构建起来了,事件也是这样传递的。

Activity -> PhoneWindow -> DecorView 

1. View 树的事件传递

 

DecorView -> ViewGroup ->ViewGroup-> ... -> View

事件传递的顺序理解之后,我们来看看事件是如何消费的?事件这个传递方向一定是从Activity到跟View 再基于View 的树形结构一个一个的分发到最后的集体的View。

这样的事件分发机制逻辑非常清晰, 如果最后分发到View,如果这个View也没有消费这个事件,那么这个事件会按照反方向回传,最终传回给Activity,如果最后 Activity 也没有处理,本次事件才会被抛弃

一个没有任何View消费的时间。会走一大圈在,大概这个样子
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ViewGroup-> ... -> View

Activity <- PhoneWindow <- DecorView <- ViewGroup <- ViewGroup <- ... <View

通过这个流程,我们就可以看到View事件分发已经非常清晰了。

这是一个非常简单的 责任链模式 就可以实现。

每个层级各司其职, 如果需要处理就拦截下来自己干,如果不需要处理或者不确定就交给责任链中下一个对象。 这种设计是非常精巧的,上层View既可以直接拦截该事件,自己处理,也可以先询问(分发给)子View,如果子View需要就交给子View处理,如果子View不需要还能继续交给上层View处理。既保证了事件的有序性,又非常的灵活。

具体实现就非常简单了。责任链模式写起来就很容易。Android使用了三个主要方法来实现这个机制。

onInterceptTouchEvent
dispatchTouchEvent
onTouchEvent

Activity 和 View 没有事件拦截:

Activity 一般不会拦截,从设计角度,这个其实是View的分发,如果View需要拦截,可以从View层面去处理就可以,而 Activity 其实一个管家,相当于一个桥梁,这样Activity的逻辑会更清晰一些。

View最为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截。

这样我们就能更直白的看懂View事件的传递机制了。

三、 事件分发机制源码

了解了上面这些,还有责任链模式,在开发过程中,如果遇到问题,可以查看源码,具体情况具体分析,就非常容易了。了解了上面的整体流程其实看,源码也非常简单。其实整个源码就是为了实现上面说的内容而基于责任链模式写的,其实不看源码我们自己写一个类似的逻辑也会非常容易。

理解上面的分析其实很多面试题都可以迎刃而解了。

四、 面试题回答

ViewGroup 和 ChildView 同时监听( View.onClick()),哪个会执行?

事件如果没有拦截,就会一层一层传递,最后传递到ChildView 消费掉。

因为是责任链模式,所以不管消费不消费都会返回回去,告诉外层View我消费了,ViewGroup看到ChildView消费了 就会忽略,不做任何事情了。

事件分发面试题还是有一些的,这里没有扩展太复杂,只是想把最最最基础的逻辑讲清楚吗,理解了这个逻辑,再去分析各种面试题都会手到擒来的,希望对你有帮助。