Android中View和ViewGroup事件分发拦截机制完美分析_viewgroup事件拦截_加油勇士的博客-程序员资料

技术标签: Android  

出自:http://www.cnblogs.com/linjzong/p/4191891.html

Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。

View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的处理流程:首先得有个结构模型概念:ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:

 

当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。来个简单版的代码加深理解:

复制代码
 /**
     * ViewGroup
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管
        View[] views=getChildView();
        for(int i=0;i<views.length;i++){
    
//判断下Touch到屏幕上的点在该子View上面 
if(...){
if(views[i].dispatchTouchEvent(ev)) return true;
} } ...
//其他处理,在此不管 } /** * View * @param ev * @return */ public boolean dispatchTouchEvent(MotionEvent ev){ ....//其他处理,在此不管 return false; }
复制代码

在此可以看出,ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件,事实上子View的dispatchTouchEvent方法真正执行的代码是这样的

复制代码
/**
     * View
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管
        return onTouchEvent(event);
    }
复制代码

一般情况下,我们不该在普通View内重写dispatchTouchEvent方法,因为它并不执行分发逻辑。当Touch事件到达View时,我们该做的就是是否在onTouchEvent事件中处理它。

那么,ViewGroup的onTouchEvent事件是什么时候处理的呢?当ViewGroup所有的子View都返回false时,onTouchEvent事件便会执行。由于ViewGroup是继承于View的,它其实也是通过调用View的dispatchTouchEvent方法来执行onTouchEvent事件。

 

在目前的情况看来,似乎只要我们把所有的onTouchEvent都返回false,就能保证所有的子控件都响应本次Touch事件了。但必须要说明的是,这里的Touch事件,只限于Acition_Down事件,即触摸按下事件,而Aciton_UP和Action_MOVE却不会执行。事实上,一次完整的Touch事件,应该是由一个Down、一个Up和若干个Move组成的。Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理。当所有子View的onTouchEvent都返回false时,这次的Touch请求就由根ViewGroup,即Activity自己处理了。

看看改进后的ViewGroup的dispatchTouchEvent方法

复制代码
View mTarget=null;//保存捕获Touch事件处理的View
    public boolean dispatchTouchEvent(MotionEvent ev) {

        //....其他处理,在此不管
        
        if(ev.getAction()==KeyEvent.ACTION_DOWN){
            //每次Down事件,都置为Null

if(!onInterceptTouchEvent()){
mTarget=null; View[] views=getChildView(); for(int i=0;i<views.length;i++){ if(views[i].dispatchTouchEvent(ev)) mTarget=views[i]; return true; }
} }
//当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move if(mTarget==null){ return super.dispatchTouchEvent(ev); } //...其他处理,在此不管 if(onInterceptTouchEvent()){
         //...其他处理,在此不管    
}
//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
        return mTarget.dispatchTouchEvent(ev);

    }
复制代码

 

ViewGroup还有个onInterceptTouchEvent,看名字便知道这是个拦截事件。这个拦截事件需要分两种情况来说明:

1.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该ViewGroup的所有下发操作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。由于mTarge为null,该ViewGroup的onTouchEvent事件被执行。这种情况下可以把这个ViewGroup直接当成View来对待。

2.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Acion为Down的Touch事件都返回false,其他的都返回True,这种情况下,Down事件能正常分发,若子View都返回false,那mTarget还是为空,无影响。若某个子View返回了true,mTarget被赋值了,在Action_Move和Aciton_UP分发到该ViewGroup时,便会给mTarget分发一个Action_Delete的MotionEvent,同时清空mTarget的值,使得接下去的Action_Move(如果上一个操作不是UP)将由ViewGroup的onTouchEvent处理。

情况一用到的比较多,情况二个人还未找到使用场景。

从头到尾总结一下:

1.Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

2.ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

4.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

5.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

6.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

 

另外,上文所列出的代码并非真正的源码,只是概括了源码在事件分发处理中的核心处理流程,真正源码各位可以自己去看,包含了更丰富的内容。

 补充:

“触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。”,这里补充下其实UP事件是可能为0个的。

 

最近刚好在做一个手势放大缩小移动图片的Demo,对此有了更多的理解。对于onInterceptTouchEvent事件,它的应用场景在很多带scroll效果的ViewGroup中都有体现。设想一下再一个ViewPager中,每个Item都是个ImageView,我们需要对这些ImageView做Matrix操作,这不可避免要捕获掉Touch事件,但是我们又需要做到不影响ViewPager翻页效果,这又必须保证ViewPager能捕获到Move事件,于是,ViewPager的onInterceptTouchEvent会对Move事件做一个过滤,当适当条件的Move事件(持续若干事件或移动若干距离,这里我没读源码只是猜测)触发时,并会拦截掉,返回子View一个Action_Cancel事件。这个时候子View就没有Up事件了,很多需要在Up中处理的事物要转到Cancel中处理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chenya866/article/details/52573092

智能推荐

疯狂的程序员-第二十五章_iteye_13777的博客-程序员资料

说那男生,个子不高,其貌不扬,说话声音极小,样子老实本分,问他:“会C++吗?”还便真老老实实地回答:“不会,我会VB。”总之怎么看怎么像块踏踏实实做技术的材料。再说那女生,问她:“会C++吗?”她答:“我编程的水平在这个城市应该是数一数二的,怎么不会C++。”这真是语不惊人死不休吓了绝影一跳,想自己在这学校里呆了四年,论写程序的水平,自我感觉还不错,至少在学校里没有他看得上的人,听了这女...

html js css如何关联_程序员的浪漫表白代码(JS+CSS+HTML)附带源码_weixin_39564617的博客-程序员资料

以下是html代码:&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;&lt;html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;head&gt...

网络流dinic最大流板子 bzoj[1001]_bzoj网络流最大流_Richard__Luan的博客-程序员资料

传送门bzoj【1001】最小割最大流据说此题是对偶图最短路~~~最大流过了,就当是练网络流了吧代码如下#include&amp;lt;cstdio&amp;gt;#include&amp;lt;iostream&amp;gt;#include&amp;lt;queue&amp;gt;#include&amp;lt;cstring&amp;gt;using namespace std;int n,m;int fst[10000...

SOFA安装_Murphy _的博客-程序员资料

先上官网教程本文是在window10平台下编译安装SOFA,仅供参考学习,具体安装请根据自己版本等进行相应调整,报错解决方法也不绝对正确,目前只是成功运行了runSofa.exe,见下图一、依赖准备所需依赖及版本列表(版本以博主此次安装版本为例 其他版本类似但不同望周知):依赖项版本用途visual studiovisual studio 2019企业版VS编译sofa稳定版 v19.12sofa源码sofa-win-dependencides官网链接直

upstart 分析 and linux启动分析 (非常详细)_initctl emit_sdcxyz的博客-程序员资料

前言众所周知,/sbin/init 是linux内核启动后运行的首个用户进程,早期的init程序由sysvinit包提供。 SysVinit软件包包含了一组控制系统最基本函数的进程,它包含了系统初始化程序init,init 是系统启动时被kernel最先启动的进程,它控制着其它所有进程的启动、运行以及停止。Sysvinit 中的init daemon 是一个基于运行级别的初始化程序,它使用

Android N“直接启动”是什么神奇的功能?_cch___的博客-程序员资料

从 Android N 开始,在首次开机时,在用户尚未来得及解锁设备之前,设备可直接启动到一种名为 Direct Boot(直接启动)的新模式中。在此模式下,操作系统可以全功能运行,但不允许访问私有应用数据,只能运行经过更新、可支持直接启动功能的应用。直接启动适合我的应用吗?并非所有应用都应运行在直接启动模式下,因此,在开始编码之前,请务必先检查您的应用是否符合以下常见的用例:

随便推点

串口服务器的作用是什么?-程序员资料

串口服务器提供串口转互联网作用,可以将RS-232/485/422串口转化成TCP/IP网络接口,保持RS-232/485/422串口与TCP/IP网络接口的统计数据双向透明传输。促使串口设备可以马上具有TCP/IP网络接口作用,网络连接开展数据通讯,巨大的拓展串口设备的通讯间距。许多传统的设备如POS、ATM、刷卡机、读卡器、交换机、加油机、RTU、数控机床、测试仪表等不具备联网功能,只具备RS-232/RS-485/RS-422/RS-232/RS-485等串行接口。主要实现以太网和各种窗口之间

Linux主机下配置Oracle 10G开机时自动启动服务_liyf155的博客-程序员资料

在Linux上安装了Oracle 10G,不像Windows系统会创建服务程序,并开机时自动启动相关的Oracle应用服务,所以Linux下需要手动去配置。步骤如下:一、使用root用户修改/etc/oratab 文件:$ gedit /etc/orataborcl:/Oracle/app/product/10.2.0/db_1:N改为:orcl:/Oracle/...

java jpanel 叠加,如何在Java中堆叠/覆盖jPanel?_whatis真实的博客-程序员资料

I am really new to GUI programming in Java, I did a lot of research and I couldn't find an answer to this problem.I have a simple JFrame with a menu, and inside this JFrame I have a JPanel with a log ...

过滤器不过滤某些地址实例_藏红的博客-程序员资料

过滤器不过滤某些地址实例 的一个实例

div+css静态网页设计——环境保护--水生态环保(5页) 环境网页设计作业 简单大学生静态HTML网页作品_环境的三张网页_优质内容贡献的博客-程序员资料

HTML5期末大作业:环境保护网站设计——环境保护–水生态环保(5页) 环境网页设计作业 简单大学生静态HTML网页作品常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业, 可满足大学生网页大作业网页设计需求, 喜欢的可以下载!作品介绍

SAX读取xml文件的方法总结_eternity668的博客-程序员资料

Xml文件的读取有多种方法,每种方法采用不同的操作来读取xml文件。本次将的是SAX的方法读取xml文件。原理:Sax对xml文件的读取是采用事件机制的,当某事件发生时,就会自动的触发定义好的事件处理方法。在一定程度上与消息机制是相似的。这也是为什么我们要写事件处理类(handler类)的原因。 基本的事件有:Document,Element,characters三种最基本的事件Xml文件读

推荐文章

热门文章

相关标签