概述

Android事件传递机制也是Android系统中比较重要的一块,事件类型有很多种,这里主要讨论TouchEvent的事件在framework层的传递处理机制。因为对于App开发人员来说,理解framework层的事件传递机制,就差不多了。

带着问题来思考整个事件分发过程。

1、为什么要有事件分发过程?

当Android设备的屏幕,接收到触摸的动作时,屏幕驱动把压力信号(包括压力大小,压力位置等)传递给系统底层,然后操作系统经过一系列的处理,然后把触摸事件一层一层的向上传递,最终事件会被准确的传递到产生事件的对象上,系统会遍历每一个View对象,然后计算触摸点在哪一个View中。比如A和B两个View,是兄弟View,AView产生的触摸事件,是不会被分发到B上面的。

2、怎么看待事件序列?

在Android系统中,一个单独的事件基本上是没什么作用的,只有一个事件序列,才有意义。一个事件序列正常情况下,定义为 DOWN、MOVE(0或者多个)、UP/CANCEL。事件序列以DOWN事件开始,中间会有0或者多个MOVE事件,最后以UP事件或者CANCEL事件结束。

DOWN事件作为序列的开始,有一个很重要的职责,就是寻找事件序列的接受者,怎么理解呢?framework 在DOWN事件的传递过程中,需要根据View事件处理方法(onTouchEvent)的返回值来确定事件序列的接受者。如果一个View的onTouchEvent事件,在处理DOWN事件的时候返回true,说明它愿意接受并处理该事件序列。

3、Android的framework层如何处理事件的分发过程?

触摸事件到了framework层之后,首先会被传递到Activity,然后Activity会把事件委托给它内部的Window对象进行分发处理,而Window对象又会委托它内部的DecorView进行事件分发处理。我们都知道,DecorView是整棵View树的根节点,所以整个事件传递过程的复杂度就是事件在View树种分发传递的复杂度。 Android View框架提供了3个对事件的主要操作概念。

1、事件的分发机制,dispatchTouchEvent。主要是parent根据触摸事件的产生位置,以及child是否愿意负责处理该系列事件等状态,向其child分发事件的机制。

2、事件的拦截机制,onInterceptTouchEvent。主要是parent根据它内部的状态、或者child的状态,来把事件拦截下来,阻止其进一步传递到child的机制。

3、事件的处理机制,onTouchEvent。主要是事件序列的接受者(可以是一个View或者ViewGroup),对事件作出处理,并且向其parent传递处理结果的机制。

4、上述三个机制,是怎么向其调用者传递处理结果的?

在Java中,传递计算结果,有很多种途径,这里采用的是一种适用于同步调用的方法,返回值的方法。每个机制都使用boolean类型作为其返回值,那么每个机制的每个返回值是什么含义呢。

1、事件的分发机制,dispatchTouchEvent。

true-事件被以该节点为根节点的View树成功处理,此时该事件就算是处理完成了,事件不会再向上返还给View的父节点(把事件分发过来的那个节点)。

false-以该节点为根节点的View树种,没有一个View(包括该View)成功处理了此事件,所以事件会向上返还给View的父节点(把事件分发过来的那个节点)。

2、事件的拦截机制,onInterceptTouchEvent。主要是parent根据它内部的状态、或者child的状态,来把事件拦截下来,阻止其进一步传递到child的机制。

true-当前ViewGroup(因为View中没有该方法,而没有child的VIew也不需要有拦截机制)希望该事件不再传递给其child,而是希望自己处理。

false-当前ViewGroup不准备拦截该事件,事件正常向下分发给其child。

3、事件的处理机制,onTouchEvent。主要是事件序列的接受者(可以是一个View或者ViewGroup),对事件作出处理,并且向其parent传递处理结果的机制。

true-表示该View成功处理了该事件,该处理结果会向上通知给其parent。

false-表示该View没有成功处理该事件,那么它的parent会有机会来处理该事件(parent标记为事件序列接受者,parent 的 onTouchEvent 在 Down 事件时返回true)。

源代码分析

源代码基于SDK 23

View:

1、dispatchTouchEvent:

/** 把事件分发到目标对象,因为这里是View对象,默认不含有child,所以这里他会把事件分发给自己 */

public boolean dispatchTouchEvent(MotionEvent event);

源代码:

不给出,有兴趣的读者执行查阅SDK

伪代码:

public boolean dispatchTouchEvent(MotionEvent event){
boolean result = false;
//如果有事件监听器,先让监听器处理事件。
if (mOnTouchListener.onTouch(event)) {
//如果监听器成功处理了该事件,处理结果设置为true。
result = true;
}
//如果没有监听器,就调用自身的onTouchEvent方法来处理事件。
if (!resutlt && onTouchEvent(event)) {
//如果自身的onTouchEvent成功处理事件,处理结果设置为true。
result = true;
}
return result;
}

ViewGroup:

1、onInterceptTouchEvent

/** 默认实现是返回false,也就是默认不拦截任何事件 */

public boolean onInterceptTouchEvent(MotionEvent ev);

2、dispatchTouchEvent

/** 根据内部拦截状态,向其child或者自己分发事件 */

public boolean dispatchTouchEvent(MotionEvent ev);

源代码:

不给出,有兴趣的读者执行查阅SDK

伪代码:

public boolean dispatchTouchEvent(MotionEvent ev) {
if (ACTION_DOWN事件 || 没有事件处理对象) {
if (允许拦截事件,该标志位由child调用requestDisallowInterceptTouchEvent<span style="font-family:微软雅黑;font-size:14px;">设置</span>) {
//查询拦截机制的结果,根据该结果来判断是否需要拦截
intercepted = onInterceptTouchEvent(ev);
} else {
//不允许拦截,那么不拦截
intercepted = false;
}
} else {
//不是DOWN,并且有处理对象,允许拦截,中断事件传递
intercepted = true;
} if (不取消 && 不拦截) {
if (ACTION_DOWN) { //找寻接收事件序列的对象,其他事件不需要再计算事件产生对象,试想一下滑动一个ListView,当手指滑动出ListView的范围时,依然还是ListView响应后续事件。
for (遍历所有childView) {
if (触摸点不在childView内部) {
continue;
}
if (childView.dispatchTouchEvent(event)) {
保存处理该事件的View,后续事件直接传递到该View,不要重新计算;
}
}
} if (还没有事件处理对象) {
//当前View树中没找到合适的child处理对象,把事件给自己处理,View.dispatchTouchEvent()就是把事件分发给自己
super.dispatchTouchEvent(event);
} else {
//传递给child
childView.dispatchTouchEvent(event);
}
} else if (拦截) {
//拦截事件,把事件给自己处理,View.dispatchTouchEvent()就是把事件分发给自己
super.dispatchTouchEvent(event);
} return 处理结果;
}

3、requestDisallowInterceptTouchEvent

/** 干涩parent的事件分发机制,通知parent,是否拦截后续事件,如果设置为true,parent就不会拦截该事件,不管什么状态。设置为false,parent走正常的拦截流程
*/

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);

源代码:

不给出,有兴趣的读者执行查阅SDK

伪代码:

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (已经是当前要设置的状态) {
// 已经处于这个状态, 假设我们的parent也是这个状态
return;
}
设置该状态;
// 传递给parent
if (有父容器) {
设置父容器的拦截状态;
}
}

自己动手

我们都知道,如果ScrollView内部嵌套ListView,那么ListView是不可以滑动的,效果如下图所示:

那么其实这就是典型的事件冲突问题,就是说,原本应该被ListView用来上下滑动的事件,被ScrollView拦截了。就导致ListView不能正常滑动。

我们来看一下ScrollView的源代码:

onInterceptTouchEvent的伪代码:

public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* 这个方法决定了我们是否要拦截事件.
* 如果返回true, onTouchEvent会被调用并且我们开始做实际的Scroll操作.
*/ /*
* 大部分循环的状态: 用户在再拖拽的状态并且正在移动手指,
* 我们希望拦截这个事件
*/
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
} //其他操作
......................
}

所以正常的上下拖拽,ScrollView都会拦截。

那么我们下面改进一下,就是当我们滑动ScrollView中非ListView的区域时,ScrollView滑动,而我们滑动ListView的时候,ListView滑动,效果看起来如下图所示:

这里解决方法如下:

既然ScrollView会拦截事件,那么当我们滑动ListView的时候,我们不希望ScrollView拦截事件,这里我们继承ListView,在onTouchEvent中,请求ScrollView不要拦截事件。

部分代码如下:

@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
break;
default:
break;
}
return true;
}

这样就可以很好的解决事件冲突的问题。

还有一种方法就是覆写parent的onInterceptTouchEvent方法,来修改事件拦截的状态。

Android View的事件分发机制探索的更多相关文章

  1. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  2. Android View的事件分发机制

    准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...

  3. Android view 的事件分发机制

    1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * ...

  4. Android View 的事件分发原理解析

    作为一名 Android 开发者,每天接触最多的就是 View 了.Android View 虽然不是四大组件,但其并不比四大组件的地位低.而 View 的核心知识点事件分发机制则是不少刚入门同学的拦 ...

  5. 【Android - 自定义View】之View的事件分发机制

    参考资料: View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829 ViewGroup事件分发:http://blog.csdn.net ...

  6. View的事件分发机制解析

    引言 Android事件构成 在Android中,事件主要包含点按.长按.拖拽.滑动等,点按又包含单击和双击,另外还包含单指操作和多指操作.全部这些都构成了Android中的事件响应.总的来说.全部的 ...

  7. Android中的事件分发机制

    Android中的事件分发机制 作者:丁明祥 邮箱:2780087178@qq.com 这篇文章这周之内尽量写完 参考资料: Android事件分发机制完全解析,带你从源码的角度彻底理解(上) And ...

  8. 浅谈Android中的事件分发机制

    View事件分发机制的本质就是就是MotionEvent事件的分发过程,即MotionEvent产生后是怎样在View之间传递及处理的. 首先介绍一下什么是MotionEvent.所谓MotionEv ...

  9. Android中View的事件分发机制

    简介 事件也称MotionEvent,事件分发机制就是对MotionEvent事件的分发过程,即当一个MotionEvent发生之后,系统需要把这个事件传递给一个具体的View. 点击事件的分发过程由 ...

随机推荐

  1. Django自动化测试平台项目案例

    引言 在项目组待了近半年时间,根据自己的感受和体验,做了一个辅助测试的工具(数据预报平台),主要目的是针对目前项目接口文档缺失,自动化预报脚本无法可视化,资源循环利用,统一管理化. 业务背景 我们项目 ...

  2. 【WPF学习】第四十二章 透明

    WPF支持真正的透明效果.这意味着,如果在一个性质或元素上层叠另外几个形状或元素,并让所有这些形状和元素具有不同的透明度,就会看到所期望的效果.通过该特性能够创建透过上面的元素可以看到的的图像背景,这 ...

  3. socket实现文件上传(客户端向服务器端上传照片示例)

    本示例在对socket有了基本了解之后,可以实现基本的文件上传.首先先介绍一下目录结构,server_data文件夹是用来存放客户端上传的文件,client_data是模拟客户端文件夹(目的是为了测试 ...

  4. Luogu P1330 封锁阳光大学 (黑白染色)

    题意: 无向图,给一个顶点染色可以让他相邻的路不能通过,但是相邻顶点不能染色,求是否可以让所有的路不通,如果可以求最小染色数. 思路: 对于无向图中的每一个连通子图,都只有两种染色方法,或者染不了,直 ...

  5. ionic中的http,jsonp的使用

    要导入 import { HttpModule,JsonpModule } from '@angular/http'; 到app.module.ts       http,jsonp的区别 第一:在U ...

  6. 关于在osgearth 中 出现 arial.ttf : file not handled 的问题

    这是由于配置osg时 freetype 插件没有配置到位. 我个人的解决方法 打开CMAKE,点击advance,不勾选OSG_TEXT_USE_FONTCONFIG. 同时将freetype路径设置 ...

  7. 使用Java8 Files类读写文件

    Java8 Files类的newBufferedReader()和newBufferedWriter()方法 这两个方法接受Path类型的参数.Path 类是Java8 NIO中的接口.可以由Path ...

  8. Python3(九) 闭包

    一. 一切皆对象 函数式编程并没有标准定义,如果代码非常繁琐则考虑使用. 学习闭包的概念,不是python独有的. 其他大多数语言中的函数只是一段可执行的代码,并不是对象. python中的函数是对象 ...

  9. Hexo搭建静态博客踩坑日记(二)

    前言 Hexo搭建静态博客踩坑日记(一), 我们说到利用Hexo快速搭建静态博客. 这节我们就来说一下主题的问题与主题的基本修改操作. 起步 chrome github hexo git node.j ...

  10. 数据算法 --hadoop/spark数据处理技巧 --(17.小文件问题 18.MapReuce的大容量缓存)

    十七.小文件问题 十八.MR的大容量缓存 在MR中使用和读取大容量缓存,(也就是说,可能包括数十亿键值对,而无法放在一个商用服务器的内存中).本次提出的算法通用,可以在任何MR范式中使用.(eg:MR ...