共计 2992 个字符,预计需要花费 8 分钟才能阅读完成。
原文链接
简简单单讲清楚 android 事件分发。
什么叫事件分发机制?
事件分发是:当发生了一个事件时,在屏幕上找到一个合适的控件来处理这个事件的过程。
因为一个界面上控件如此之多,发生一个事件后总要寻找一个合适来处理事件吧。这个过程就叫做事件分发的机制。
常见事件
那么屏幕上都会发生什么事件呢?来看下经常要处理的 4 种事件(这些事件在 android 中会被封装成 MotionEvent 对象):
事件
简介
ACTION_DOWN
手指 初次接触到屏幕 时触发。
ACTION_MOVE
手指 在屏幕上滑动 时触发,会会多次触发。
ACTION_UP
手指 离开屏幕 时触发。
ACTION_CANCEL
事件 被上层拦截 时触发。
屏幕中的控件
原文链接
为了在屏幕中如何寻找合适的处理事件的控件,我们先来看下屏幕中都有什么控件。
假设我们写了如下的一个布局:
<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 结构:
原文链接
上面的代码运行到界面上由图中这几部分组成:
可以看到在上面的 View 结构中莫名多出来的两个东西,PhoneWindow 和 DecorView,这两个我们并没有在 Layout 文件中定义过,但是为什么会存在呢?
1. PhoneWindowPhoneWindow 是 Window 的唯一实现类,是所有视图的最顶层容器,视图的外观和行为都归他管,不论是背景显示,标题栏还是事件处理都是他管理的范畴。2. DecorViewDecorView 是 PhoneWindow 的一个内部类,就是跟在 PhoneWindow 身边专业为 PhoneWindow 服务的,除了自己要干活之外,也负责消息的传递,PhoneWindow 的指示通过 DecorView 传递给下面的 View,而下面 View 的信息也通过 DecorView 回传给 PhoneWindow。
事件分发流程
在如上图 View 的树形结构中,事件发生时,最先由 Activity 接收,然后再一层层的向下层传递,直到找到合适的处理控件。大致如下:
Activity -> PhoneWindow -> DecorView -> ViewGroup -> … -> View
但是如果事件传递到最后的 View 还是没有找到合适的 View 消费事件,那么事件就会向相反的方向传递,最终传递给 Activity,如果最后 Activity 也没有处理,本次事件才会被抛弃:
Activity <- PhoneWindow <- DecorView <- ViewGroup <- … <- View
Android 中事件具体是怎样进行分发的
原文链接
当事件发生时,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-theoryhttps://juejin.im/entry/58df5b33570c35005798493chttps://juejin.im/entry/580042082e958a0055b6cbbchttps://blog.csdn.net/carson_ho/article/details/54136311https://www.jianshu.com/p/31e20def82c2https://segmentfault.com/a/1190000004981942https://zhuanlan.zhihu.com/p/27608989http://gityuan.com/2015/09/19/android-touch/http://wuxiaolong.me/2015/12/19/MotionEvent/
推荐阅读:推荐阅读:
Android 开发艺术探索