简简单单讲清楚android事件分发。css
事件分发是:当发生了一个事件时,在屏幕上找到一个合适的控件来处理这个事件的过程。html
由于一个界面上控件如此之多,发生一个事件后总要寻找一个合适来处理事件吧。这个过程就叫作事件分发的机制。java
那么屏幕上都会发生什么事件呢?来看下常常要处理的4种事件(这些事件在android中会被封装成 MotionEvent 对象):android
事件 | 简介 |
---|---|
ACTION_DOWN | 手指 初次接触到屏幕 时触发。 |
ACTION_MOVE | 手指 在屏幕上滑动 时触发,会会屡次触发。 |
ACTION_UP | 手指 离开屏幕 时触发。 |
ACTION_CANCEL | 事件 被上层拦截 时触发。 |
为了在屏幕中如何寻找合适的处理事件的控件,咱们先来看下屏幕中都有什么控件。git
假设咱们写了以下的一个布局:segmentfault
<com.xyl.touchevent.test.RootView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="300dp" android:background="#4E5268" android:layout_margin="20dp" tools:context="com.xyl.touchevent.MainActivity"> <com.xyl.touchevent.test.ViewGroupA android:background="#95C3FA" android:layout_width="200dp" android:layout_height="200dp"> <com.xyl.touchevent.test.View1 android:background="#BDDA66" android:layout_width="130dp" android:layout_height="130dp"/> </com.xyl.touchevent.test.ViewGroupA> <com.xyl.touchevent.test.View2 android:layout_alignParentRight="true" android:background="#BDDA66" android:layout_width="80dp" android:layout_height="80dp"/> </com.xyl.touchevent.test.RootView>
View结构:oop
上面的代码运行到界面上由图中这几部分组成:布局
能够看到在上面的View结构中莫名多出来的两个东西,PhoneWindow 和 DecorView ,这两个咱们并无在Layout文件中定义过,可是为何会存在呢?spa
1. PhoneWindow
PhoneWindow是 Window 的惟一实现类,是全部视图的最顶层容器,视图的外观和行为都归他管,不管是背景显示,标题栏仍是事件处理都是他管理的范畴。
2. DecorView
DecorView是 PhoneWindow 的一个内部类,就是跟在 PhoneWindow 身边专业为 PhoneWindow 服务的,除了本身要干活以外,也负责消息的传递,PhoneWindow 的指示经过 DecorView 传递给下面的 View,而下面 View 的信息也经过 DecorView 回传给 PhoneWindow。
在如上图View的树形结构中,事件发生时,最早由Activity接收,而后再一层层的向下层传递,直到找到合适的处理控件。大体以下:.net
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View |
---|
可是若是事件传递到最后的View仍是没有找到合适的View消费事件,那么事件就会向相反的方向传递,最终传递给Activity,若是最后 Activity 也没有处理,本次事件才会被抛弃:
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View |
---|
当事件发生时,ViewGroup会在dispatchTouchEvent方法中先看本身可否处理事件,若是不能再去遍历子View查找合适的处理控件。若是到最后result仍是false,表示全部的子View都不能处理,才会调用自身的onTouchEvent来处理。
根据上面提到的原理,ViewGroup事件传递的伪代码以下:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean result = false; // 默认状态为没有消费过 if (!onInterceptTouchEvent(ev)) { // 若是没有拦截交给子View result = child.dispatchTouchEvent(ev); } if (!result) { // 若是事件没有被消费,询问自身onTouchEvent result = onTouchEvent(ev); } return result; }
为了方便理解,这里贴出一张网上的别人的一张图:
上面的流程中存在部分不合理内容,请你们选择性接受。
事件返回时 dispatchTouchEvent 直接指向了父View的 onTouchEvent 这一部分是不合理的,实际上它仅仅是给了父View的 dispatchTouchEvent 一个 false 返回值,父View根据返回值来调用自身的 onTouchEvent。
ViewGroup 是根据 onInterceptTouchEvent 的返回值来肯定是调用子View的 dispatchTouchEvent 仍是自身的 onTouchEvent, 并无将调用交给 onInterceptTouchEvent。
若是在遍历过程当中,发现了有能够处理事件的控件,就中止事件的传递,事件被消耗。
本文参考资料:
http://www.gcssloop.com/customview/dispatch-touchevent-theory
https://juejin.im/entry/58df5b33570c35005798493c
https://juejin.im/entry/580042082e958a0055b6cbbc
http://www.javashuo.com/article/p-nwtgajhz-no.html
https://www.jianshu.com/p/31e20def82c2
http://www.javashuo.com/article/p-hnslpwns-mp.html
https://zhuanlan.zhihu.com/p/27608989
http://gityuan.com/2015/09/19/android-touch/
http://wuxiaolong.me/2015/12/19/MotionEvent/
推荐阅读:
推荐阅读: