【Android - 自定义View】之View的事件分发机制
参考资料:
View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829
ViewGroup事件分发:http://blog.csdn.net/guolin_blog/article/details/9153747
1 概述
Android中的布局是按树形结构层级排列的,根布局往往是一个ViewGroup,如LinearLayout,其中包裹一些子View或子ViewGroup,子ViewGroup中又包裹着它的子View或子ViewGroup,依此类推。
每当触摸了屏幕上的一个“东西”,都会触发这个“东西”的dispatchTouchEvent()方法,负责事件的分发。这个方法是从它的父类View或ViewGroup中继承来的。
因此,我们想要了解Android中的事件分发机制,就需要分别了解View的事件分发机制和ViewGroup的事件分发机制。
2 View事件分发
一般地,我们给一个控件添加事件,通常会调用setOnTouchListener()和setOnClickListener()这两个方法,前者是为了监听手指的按下、抬起、滑动等细节操作,而后者只是为了监听点击控件的操作。从上面的概述可以知道,无论是哪种操作,都是通过dispatchTouchEvent()方法来分配的。
在View类的dispatchTouchEvent()方法中,先判断这个View是否注册了OnTouchListener监听器,如果注册了,则回调其中的onTouch()方法;如果没有注册,则调用View类中的onTouchEvent()方法。
OnTouchListener中的onTouch()方法是回调给程序员编写的,这个方法有一个布尔类型的返回值,如果返回true则终止本次事件的分发,否则依然会调用View类中的onTouchEvent()方法。对应的部分源码如下:
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
在View类的onTouchEvent()方法中判断这个View是否是可点击的(clickable),如果是则判断这个View是否注册了OnClickListener,如果注册了就调用其中的onClick()方法。对应的部分源码如下:
public boolean onTouchEvent(MotionEvent event) {
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
performClick();
return true;
}
return false;
}
public void performClick() {
if (li != null && li.mOnClickListener != null) {
li.mOnClickListener.onClick(this);
}
}
从上面的代码中可以看出,如果一个控件时clickable的,那么onTouchEvent()就一定会返回true,否则返回false。
因此,当我们为一个控件同时设置了setOnTouchListener()和setOnClickListener()这两个方法时,会先回调OnTouchListener中的onTouch()事件,当onTouch()返回true时,才会再回调OnClickListener中的onClick()事件。
这里还需要注意的一点是,给一个控件设置了touch事件后,每次点击它时,都会触发一系列的ACTION_DOWN、ACTION_MOVE和ACTION_UP等事件。在此过程中,如果某一个ACTION返回了false,那么后面的一系列ACTION就不会再执行了。简单的说,当dispatchTouchEvent()在进行事件分发的时候,只有前一个ACTION返回true,才会触发后一个ACTION。
3 ViewGroup事件分发
当一个事件到达一个ViewGroup的时候,会递归的在它的树级结构中查找这个事件的处理者:依次遍历这个ViewGroup中的所有子View和子ViewGroup,调用它们的dispatchTouchEvent()方法,如果判断可以处理这个事件则返回true,否则返回false;当一个ViewGroup中的所有子View和子ViewGroup的dispatchTouchEvent()方法都返回false,则这个ViewGroup的dispatchTouchEvent()也返回false。
ViewGroup中有一个方法叫做onInterceptTouchEvent(),它标识这个ViewGroup是否要拦截其下的View的事件,默认返回的是false,表示不拦截。我们可以通过在自定义ViewGroup中重写这个方法设置其返回值为true,或者通过requestDisallowInterceptTouchEvent()方法获取拦截权限。
当ViewGroup的onInterceptTouchEvent()方法返回true时,所有属于这个ViewGroup中某个View的事件都会被这个ViewGroup拦截,即这个ViewGroup就成为了这个事件的处理者来处理这个事件。
4 综述
每当触摸了一次屏幕,根布局(ViewGroup)就会发出一次递归搜索,在其下层的布局或控件树中寻找拦截这次事件的控件,如果有控件或布局对这次事件进行了拦截,则表示这个控件或布局就是这次事件的处理者。
无论最终获取这个事件处理权的是View还是ViewGroup(以下统称为控件),都调用它的dispatchTouchEvent()方法对事件进行处理。
总之,Android中的事件分发机制的流程图如下图所示:
最后,用文字来描述一下完整的事件分发机制:
1、当我们在手机屏幕上点击了一下,就是在当前界面的Activity上点击了一下,就会触发当前Activity的dispatchTouchEvent()方法;
2、在Activity的dispatchTouchEvent()方法中,通过getWindow()方法找到当前Activity的Window对象(PhoneWindow对象),然后调用Window对象的superDispatchTouchEvent()方法;
3、在Window的superDispatchTouchEvent()方法中,通过getDecorView()方法找到当前Activity中的顶层View(顶层View一般都是ViewGroup),开始View的事件传递;
4、顶层View调用dispatchTouchEvent()方法,开始实际的事件分发,具体流程如下:
(1)判断这个ViewGroup是否拦截事件,即它的onInterceptTouchEvent()方法是否返回true,如果返回true则事件会交给这个ViewGroup处理,即它的onTouchEvent()方法会被调用;
(2)如果这个ViewGroup不拦截事件,则递归调用其下所有View的dispatchTouchEvent()方法,找到事件处理者的View;
(3)先判断目标View是否注册了OnTouchListener,如果注册了,则调用OnTouchListener中的onTouch()方法,验证其返回值;
(4)如果onTouch()方法返回false,表示View还没有将这个事件消费掉,会继续执行View的onTouchEvent()方法;如果onTouch()返回true,则不会继续执行onTouchEvent()方法,直接结束事件传递;
(5)如果这个View重写了onTouchEvent()方法,则会先调用重写的内容;
(6)如果给这个View设置了OnClickListener,则会在onTouchEvent()中的代码执行之后,调用OnClickListener中onClick()方法中的代码;
5、在上面的事件传递过程中,如果有一个View接管了事件,那么事件就不会继续向下传递,将这个View的信息层层上报到顶层View之后,结束事件传递;
6、如果顶层View发现底下的所有View都没有接管这个事件,那么这个事件最终会回传给Activity处理,即Activity的onTouchEvent()方法会被调用。
【Android - 自定义View】之View的事件分发机制的更多相关文章
- Activity Window View WindowManager关系&Touch事件分发机制
http://www.cnblogs.com/linjzong/p/4191891.html https://www.cnblogs.com/kest/p/5141817.html https://b ...
- 【Android开发坑系列】之事件分发机制
总结一下: 事件序列的定义:从手触摸屏幕(含)到离开屏幕(含)期间所发生的一系列交互事件.主要由ACTION_DOWN.ACTION_MOVE.ACTOIN_UP.ACTION_CANCEL等组成,其 ...
- Android:30分钟弄明白Touch事件分发机制
Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理. View在 ...
- Android View的事件分发机制
准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...
- Android面试收集录6 事件分发机制
转自:秋招面试宝典. 一. 基础认知 1.1 事件分发的对象是谁? 答:事件 当用户触摸屏幕时(View或ViewGroup派生的控件),将产生点击事件(Touch事件). Touch事件相关细节(发 ...
- Android开发——事件分发机制详解
0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...
- 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...
- 【朝花夕拾】Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/10998855.html]谢谢! 在自定义View中,经常需要处理Android事件分发的问题, ...
- 【朝花夕拾】Android自定义View篇之(七)Android事件分发机制(下)滑动冲突解决方案总结
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11072989.html],谢谢! 前面两篇文章,花了很大篇幅讲解了Android的事件分发机制 ...
随机推荐
- Object 的方法总结
1.Object.create() Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__. 用法:Object.create( proto, [pro ...
- tomcat无法启动的原因
一.排查思路 最直接也是最有效的办法:看console控制台 这是我看到的原因,我先想到是不是web.xml里的url-pattern里的命名是不是冲突 因为我在这个项目之前写了一个项目,用的是同一个 ...
- API gateway 之 kong 基本操作 (三)
一.演示环境准备 1.nginx配置 [root@nginx conf.d]# pwd /etc/nginx/conf.d [root@nginx conf.d]# ls conf_bak kong_ ...
- 在VMware15中安装虚拟机并使用Xshell连接到此虚拟机(超详细哦)
首先点击创建新的虚拟机. 此处默认, 点击下一步 默认, 点击下一步 此处可以设置你的虚拟机名称和安装位置(强烈建议不要将安装位置放在系统盘). 此处可根据自己的电脑配置来设置(建议2,4),后续可以 ...
- git .gitignore详解
1.最近使用git又遇到一个陷阱: 场景:A和B使用的不同的编译器做的同一个解决方案下的不同的项目工程,刚开始没考虑到版本问题,后来发现A上传的csproj在B需要做很麻烦修改才能打开,后来想到各自用 ...
- [考试反思]0926csp-s模拟测试52:审判
也好. 该来的迟早会来. 反思再说吧. 向下跳过直到另一条分界线 %%%cbx也拿到了他的第一个AK了呢. 我的还是遥不可及. 我恨你,DeepinC. 我恨透你了.你亲手埋葬所有希望,令我无比气愤. ...
- 大数据之路week01--自学之集合_2(列表迭代器 ListIterator)
列表迭代器: ListIterator listerator():List集合特有的迭代器 该迭代器继承了Iterator迭代器,所以,就可以直接使用hasNext()和next()方法 特有功能: ...
- MySQL原生PHP操作-天龙八步
<?php //1.第一步[建立连接] $conn = mysqli_connect('localhost','root','123456') or die('数据库连接失败!'); //2.第 ...
- variable precision SWAR算法
计算二进制形式中1的数量这种问题,在各种刷题网站上比较常见,以往都是选择最笨的遍历方法“蒙混”过关.在了解Redis的过程中接触到了variable precision SWAR算法(以下简称VP-S ...
- 网站搭建 - 虚拟机的安装 - Linux 本地网站搭建第一步
搭建网站-1-域名申请参见公众号 生物信息系统(swxxxt) 搭建网站-域名绑定见稍后的一章,就是直接点解析,然后就完事了,可以不看的. 首先准备材料: 先装虚拟机,会要求重启,那就重启吧,安装界面 ...