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

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. [ZJOI2010]基站选址

    题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范 ...

  2. UVA1658:Admiral

    题意:给定一个有向带权图,求两条不相交(无公共点)的路径且路径权值之和最小,路径由1到v 题解:这题的关键就在于每个点只能走一遍,于是我们想到以边换点的思想,用边来代替点,怎么代替呢? 把i拆成i和i ...

  3. hdu5631 BestCoder Round #73 (div.2)

    Rikka with Graph  Accepts: 123  Submissions: 525  Time Limit: 2000/1000 MS (Java/Others)  Memory Lim ...

  4. [luoguP3729]曼哈顿计划EX

    来自FallDream的博客,未经允许,请勿转载,谢谢. 艾登拥有一个计算机网络,每一台计算机都至少有着Intel Xeon E50 v40 + 40路GTX10800Titan的恐怖配置,并由无线网 ...

  5. 利用 socket 发送 get/post 请求

    思路:利用 fsockopen 函数与要请求的主机建立一个通信通道,再将请求行.头信息.主体信息通过这个通道传输给主机实现请求的发送.利用这种方式发送 get 请求就是常说的小偷程序,发送 post ...

  6. python 二维数组遍历

    import numpy as np world=np.zero([5,5]) for i in range(0,world.shape[0]) for j in range(0,world.shap ...

  7. 简述RIP路由协议和OSPF路由协议的相同点和不同点。

    路由协议分为静态路由协议和动态路由协议.动态路由协议有很多种,如RIP.OSPF.EIGRP等. 1.RIP(路由信息协议)是路由器生产商之间使用的第一个开放标准.RIP有两个版本:RIPv1和RIP ...

  8. Python之作业购物车

    作业之购物车优化 购物车优化要求如下: 用户入口: 启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就 ...

  9. Weekly Contest 75题解

    Q1. Rotate String(796) We are given two strings, A and B. A shift on A consists of taking string A a ...

  10. Spring @Component的作用详细介绍

    @component 作用 1.@controller 控制器(注入服务)2.@service 服务(注入dao)3.@repository dao(实现dao访问)4.@component (把普通 ...