请尊重分享成果,转载请注明出处:

http://blog.csdn.net/hejjunlin/article/details/52298780

上篇分析了View的事件分发流程,留了一个问题:如果上面的EventButton继承TextView的话,按下抬起,会有一个现象,我可以告诉大家现象:就是只有dispatchTouchEvent ACTION_DOWN,onTouch ACTION_DOWN,onTouchEvent ACTION_DOWN这三个,你移动,或者抬起,是没有MOVE,或者UP的。现在说下答案:当时我们在MainActivity中用到了 EventButton.setOnTouchListener(OnTouchListener l)事件监听,返回的是false,表示啥意思,ACTION_DOWN一次支持到switch中去,直接return了false,告诉父view,我处理不了,你不要派任务(事件传递)给我了,所以它只接到了ACTION_DOWN事件,接不到其他的MOVE或者UP,那问题来了,为啥继承Button可以呢?看Button源码,发现啥也没干,就继承了TextView,但用了自己的 com.android.internal.R.attr.buttonStyle,进去一看,发现两个点,一是focusable是true的,二是android:clickable也是true的,这可是问题的答案呢,可查看Android View框架总结(二)View焦点,如何是isfocusable的话,就能先于父view获取到事件。所以它是有MOVE和UP的。有兴趣的,可以把MainActivity中EventButton.setOnTouchListener(OnTouchListener l)事件监听返回true,这个EventButton就能有MOVE和UP了,可以自己动手试试,你看到的才是答案。

<style name="Widget.Button">
    <item name="android:background">@android:drawable/btn_default</item>
    <item name="android:focusable">true</item>
    <item name="android:clickable">true</item>
    <item name="android:textSize">20sp</item>
    <item name="android:textStyle">normal</item>
    <item name="android:textColor">@android:color/button_text</item>
    <item name="android:gravity">center_vertical|center_horizontal</item>
</style> 

前面是上篇遗留的问题,本篇开始分析ViewGroup事件分发(PS:本篇文章中源码均是android 6.0,请知晓)

  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent
  • ViewGroup 事件的分发机制流程图
  • 案例
  • 案例流程图

dispatchTouchEvent

ViewGroup.java -> dispatchTouchEvent()









onInterceptTouchEvent

ViewGroup.java -> onInterceptTouchEvent()

虽然以上代码都有注释,但是还有几个地方说明下:

  • if (!canViewReceivePointerEvents(child)

    这里表示判断当前的down、POINTER_DOWN、HOVER_MOVE三个事件的坐标点是否落在了子控件上,如果落在子控件上,if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))通过dispatchTransformedTouchEvent传递事件,交由子控件判断是否传递或自己消费处理。如果dispatchTransformedTouchEvent返回true,表示子控件已消费处理,并添加此子控件View到触摸链表,并放置链表头,并结束遍历子控件。newTouchTarget = addTouchTarget(child, idBitsToAssign);false表示未处理。

  • onInterceptTouchEvent表示是否拦截当前事件,返回true表示拦截,如果拦截了事件,那么将不会分发给子View。如:ViewGroup拦截了这个事件,那么所有事件都由该ViewGroup处理,它内部的子View将不会获得事件的传递。ViewGroup是默认不拦截事件的,注意:View是没有这个方法的,也即是说,继承自View的一个子View不能重写该方法,也无需拦截事件,因为它下面没有View了,它要么处理事件要么不处理事件,所以最底层的子View不能拦截事件。

  • 子View消耗了ACTION_DOWN事件,DOWN,MOVE,UP都是一个回合的事件,

    DOWN -> onInterceptTouchEvent -> true 就不向下传事件,且onInterceptTouchEvent不再调用

    DOWN -> onInterceptTouchEvent -> false 传下去

    MOVE -> onInterceptTouchEvent -> true 不传

    每个回合都要经历ViewGroup中的onInterceptTouchEvent的判断是否要拦截接下来的这个子View的事件(当然注意前提条件是每次都是返回false不拦截时,打个不恰当的比喻:这个就好像你买六合彩一样,要是你中了大奖,你至少一段时间内,你都不会再买了,如果没中,那就每次都要买);或者,如果第一次DOWN事件后,由ViewGroup本身消费掉了,那mFirstTouchTarget就为null,为空的话,说明ViewGroup不想这个事件传到它的子view中去,此后MOVE或者UP事件都将在ViewGroup这层自己处理,不会传到子view中去。

  • 打个比喻(对照事件传递机制来),国家(中央)发了一份电报到省里面(相当于一次事件DOWN,MOVE,UP其一都和),关于贫困补助某地区的,如果钱比较多,省里面觉得这油水得捞一捞(拦截onInterceptTouchEvent 返回true),于是把这个事件拦截下,自己消费掉了,直接return true,告诉上面,这个事件消耗完毕,反正上面也不知道(暂且这么说吧),下一次,国家(中央)又发了一份电报到省里面,,关于贫困补助某地区的,这次钱比较少,省里面觉得没啥捞的(不拦截 拦截onInterceptTouchEvent 返回false),就把这个消息原封不动发向了地方政府,地方政府消费这个事件,然后返回true。

dispatchTransformedTouchEvent

ViewGroup.java -> dispatchTransformedTouchEvent()



当传递进来的子View不为null时,就会调用子View的dispatchTouchEvent(event)方法进行事件分发,事件交给子View处理,也即是说,子Viwe符合父view不拦截条件时,事件就会在这里传递给了子View来处理,完成了ViewGroup到子View的事件传递,上面多次调用child.dispatchTouchEvent,这其实就回到上一篇文章中view.dispatchTouchEvent中,而昨天已经分析,view.dispatchTouchEvent接下来会调用view.onTouchEvent返回处理结果,当view事件处理完毕,就会返回一个handled给ViewGroup,就是告诉ViewGroup,是否它消费了这个事件,假如子View的onTouchEvent()返回true,那么就是消耗了事件。

ViewGroup.java -> addTouchTarget()

要是觉得细节太琐碎,总结成下面一张图,看清上面的过程:

案例:

MainActivity

自定义的RelativeLayout



自定义button

布局文件

运行studio,点击按钮,输出log结果:

从log中可以看出:执行过程是从CustomRelativeLayout的dispatchTouchEvent ->CustomRelativeLayout的onInterceptTouchEvent -> EventButton的dispatchTouchEvent ->EventButton的onTouchEvent

在View上触发事件,最先捕获到事件的为View所在的ViewGroup,然后才会到View自身~

同样这个案例,总结成下面一张图,看清上面的过程:

总结

1、一个事件传递从ACTION_DOWN开始,中间有若一个或者多个ACTION_MOVE,最后以ACTION_UP结束。

2、ViewGroup默认不拦截任何事件,所以事件能正常分发到子View处(如果子View符合条件的话),如果没有合适的子View或者子View不消耗ACTION_DOWN事件,那么接着事件会交由ViewGroup处理,并且同一事件序列之后的事件不会再分发给子View了。如果ViewGroup的onTouchEvent也返回false,即ViewGroup也不消耗事件的话,那么最后事件会交由Activity处理。即:逐层分发事件下去,如果都没有处理事件的View,那么事件会逐层向上返回。

3、如果某一个View拦截了事件,那么同一个事件序列的其他所有事件都会交由这个View处理,此时不再调用View(ViewGroup)的onIntercept()方法去询问是否要拦截了。

4、子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截

5、ViewGroup自身并没有onTouchEvent方法,在dispatchTouchEvent时,会调用dispatchTransformedTouchEvent分发到子view中去,触发ouTouchEvent,但是我们自定义View,继承ViewGroup时,是可以重写ViewGroup的onTouchEvent方法,这是为什么呢?因为ViewGroup继承View,而View是有onTouchEvent方法,所以自定义View继承ViewGroup时,就间接用到了父类的父类中的方法。这样的方处是,对于ViewGroup来说,只是负责事件分发,如果有人继承它,说明想自己控制一些事件发分流程,那么自然相关方法也都可以重写。

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

Android View框架总结(八)ViewGroup事件分发机制的更多相关文章

  1. ViewGroup事件分发机制解析

    最近在看View的事件分发机制,感觉比复杂的地方就是ViewGrop的dispatchTouchEvent函数,便对照着源码研究了一下.故名思意这个函数起到的作用就是分发事件,在具体分析之前还要说明几 ...

  2. android ViewGroup事件分发机制

    1:事件分销过程 自定义一个LinearLayout,重写dispatchTouchEvent onInterceptTouchEvent onTouchEvent,定义一个按键重写dispathcT ...

  3. 【Android开发坑系列】之事件分发机制

    总结一下: 事件序列的定义:从手触摸屏幕(含)到离开屏幕(含)期间所发生的一系列交互事件.主要由ACTION_DOWN.ACTION_MOVE.ACTOIN_UP.ACTION_CANCEL等组成,其 ...

  4. Android:30分钟弄明白Touch事件分发机制

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

  5. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  6. android view事件分发机制

    首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志 ...

  7. Android View 事件分发机制 源代码解析 (上)

    一直想写事件分发机制的文章,无论咋样,也得自己研究下事件分发的源代码.写出心得~ 首先我们先写个简单的样例来測试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个M ...

  8. Android之事件分发机制

    本文主要包括以下内容 view的事件分发 viewGroup的事件分发 首先来看两张图 在执行touch事件时 首先执行dispatchTouchEvent方法,执行事件分发. 再执行onInterc ...

  9. android菜鸟之路-事件分发机制总结(二)

    ViewGroup事件分发机制 自己定义一个LinearLayout,ImageView和Button,小二,上代码 <LinearLayout xmlns:android="http ...

随机推荐

  1. [SCOI2012]滑雪与时间胶囊

    题目描述 a180285非常喜欢滑雪.他来到一座雪山,这里分布着MMM条供滑行的轨道和NNN个轨道之间的交点(同时也是景点),而且每个景点都有一编号iii(1≤i≤N1 \le i \le N1≤i≤ ...

  2. [USACO13OPEN]照片Photo

    题目描述 Farmer John has decided to assemble a panoramic photo of a lineup of his N cows (1 <= N < ...

  3. hdu 5442 (后缀数组)

    稍微学习了下第一次用后缀数组- - , 强行凑出答案 , 感觉现在最大的问题是很多算法都不知道 ,导致有的题一点头绪都没有(就像本题).  /*推荐 <后缀数组——处理字符串的有力工具>— ...

  4. SpringCloud学习之SpringCloudBus

    一.spring-cloud-bus是什么? 回答这个问题之前,我们先回顾先前的分布式配置,当配置中心发生变化后,我们需要利用spring-boot-actuator里的refresh端点进行手动刷新 ...

  5. C#+HtmlAgilityPack+Dapper走一波爬虫

    最近因为公司业务需要,又有机会撸winform了,这次的需求是因为公司有项目申报的这块业务,项目申报前期需要关注政府发布的相关动态信息,政府部门网站过多,人工需要一个一个网站去浏览和查阅,有时候还会遗 ...

  6. TensorFlow + Keras 实战 YOLO v3 目标检测图文并茂教程

    运行步骤 1.从 YOLO 官网下载 YOLOv3 权重 wget https://pjreddie.com/media/files/yolov3.weights 下载过程如图: 2.转换 Darkn ...

  7. Python爬取腾讯新闻首页所有新闻及评论

    前言 这篇博客写的是实现的一个爬取腾讯新闻首页所有的新闻及其所有评论的爬虫.选用Python的Scrapy框架.这篇文章主要讨论使用Chrome浏览器的开发者工具获取新闻及评论的来源地址. Chrom ...

  8. ref string

    string pics=""; pSub.GetSubjectContent(pddm,ref  pics); public string GetSubjectContent(st ...

  9. 安装插件出现eclipse An internal error occurred during: "Installing Software". xxxxxxxxx

    就是你自己本来就有那个插件了 百度怎么删吧.... 看一下我这个文章 强烈建议本地安装的时候用第四种安装 http://www.cnblogs.com/ydymz/articles/7203260.h ...

  10. Linux 在线模拟器

    最近在学习Linux的一些命令的使用,但是很久之前装的Linux虚拟机被删掉了,又不想为了练习几个命令折腾一遍虚拟机.所以,就尝试地搜了一下,看看有没有在线的Linux模拟器可以使用,只要可以练习一下 ...