首先,我们需要 知道什么是ViewGroup,它和普通的View有什么区别?

  ViewGroup就是一组View的集合,它包含很多子View和ViewGroup,是Android 所有布局的父类或间接父类.

  但ViewGroup也是一个View,只不过比起View,它可以包含子View和定义布局参数的功能.

  现在,通过一个Demo演示Android中ViewGroup的事件分发机制.

  首先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下 所示:

 

  1.   publicclassMyLayoutextendsLinearLayout{
  2.       publicMyLayout(Context context,AttributeSet attrs){
  3.       super(context, attrs);
  4.       }
  5.   }
 

  然后,打开主布局文件activity_main.xml,在其中加入我们自定义的布局:

  1.  xmlns:tools="http://schemas.android.com/tools"
  2.   android:id="@+id/my_layout"
  3.   android:layout_width="match_parent"
  4.   android:layout_height="match_parent"
  5.   android:orientation="vertical" >
  6.   
  7.   android:id="@+id/button1"
  8.   android:layout_width="match_parent"
  9.   android:layout_height="wrap_content"
  10.   android:text="Button1" />
  11.   
  12.   android:id="@+id/button2"
  13.   android:layout_width="match_parent"
  14.   android:layout_height="wrap_content"
  15.   android:text="Button2" />
 

  可以看到,我们在MyLayout中添加了两个按钮,接着在MainActivity中为这两个按钮和MyLayout都注册了监听事件:

 

  1.  myLayout.setOnTouchListener(newOnTouchListener(){
  2.   @Override
  3.       publicboolean onTouch(View v,MotionEvent event){
  4.           Log.d("TAG","myLayout on touch");
  5.       returnfalse;
  6.       }
  7.   });
  8.   button1.setOnClickListener(newOnClickListener(){
  9.   @Override
  10.       publicvoid onClick(View v){
  11.           Log.d("TAG","You clicked button1");
  12.       }
  13.   });
  14.   button2.setOnClickListener(newOnClickListener(){
  15.   @Override
  16.       publicvoid onClick(View v){
  17.           Log.d("TAG","You clicked button2");
  18.       }
  19.   });
 

  我们在MyLayout的onTouch方法,和Button1、Button2的onClick方法中都打印了一句话。现在运行一下项目,效果图如下所示:

  

  分别点击一下Button1、Button2和空白区域,打印结果如下所示:

  

  当你点击按钮时,MyLayout的Touch事件并不会被触发,而在点击除按钮之外的空白部分,却能触发MyLayot的Touch事件.

  难道事件是从View传递到ViewGroup中的?现在下结论还太早.

  查阅文档,发现ViewGroup中还有一个onInterceptTouchEvent()方法,

  1.   publicboolean onInterceptTouchEvent(MotionEvent ev){
  2.    returnfalse;
  3.   }
 

  看Intercept,知道这应该是一个拦截器,拦截Touch事件的.但默认确实对所有的Touch都不拦截.

  在MyLayout中重写一下这个方法,然后返回一个true.

  

  1.  publicclassMyLayoutextendsLinearLayout{
  2.   publicMyLayout(Context context,AttributeSet attrs){
  3.    super(context, attrs);
  4.   }
  5.   @Override
  6.   publicboolean onInterceptTouchEvent(MotionEvent ev){
  7.    returntrue;
  8.    }
  9.   }
 

 

  现在再次运行项目,然后分别Button1、Button2和空白区域,打印结果如下所示:

  

  你会发现,不管点击在哪里,永远都只会触发MyLayout的Touch事件.按钮的点击事件被完全屏蔽掉了.

  所以得出结论:Touch事件会先分发到ViewGroup中,再传递到View中.

  上篇有个结论,触摸任何控件,都会调用dispatchTouchEvent()方法.其实不太准确.

  实际情况是,当点击了某个控件,首先会调用所在布局ViewGroup的dispatchTouchEvent(),在方法里,找到子View,调用子VIew的dispatchTouchEvent.

  当点击了MyLayout时,就会调用MyLayout的dispatchTouchEvent方法,但是MyLayout并没有这个方法,直到父类Linearlayout的父类ViewGroup中才有.

  

  下面是ViewGroup中dispatchTouchEvent()的源码:

  

  1. public boolean dispatchTouchEvent(MotionEvent ev) {
  2. final int action = ev.getAction();
  3. final float xf = ev.getX();
  4. final float yf = ev.getY();
  5. final float scrolledXFloat = xf + mScrollX;
  6. final float scrolledYFloat = yf + mScrollY;
  7. final Rect frame = mTempRect;
  8. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  9. if (action == MotionEvent.ACTION_DOWN) {
  10. if (mMotionTarget != null) {
  11. mMotionTarget = null;
  12. }
  13. if (disallowIntercept || !onInterceptTouchEvent(ev)) {
  14. ev.setAction(MotionEvent.ACTION_DOWN);
  15. final int scrolledXInt = (int) scrolledXFloat;
  16. final int scrolledYInt = (int) scrolledYFloat;
  17. final View[] children = mChildren;
  18. final int count = mChildrenCount;
  19. for (int i = count - 1; i >= 0; i--) {
  20. final View child = children[i];
  21. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
  22. || child.getAnimation() != null) {
  23. child.getHitRect(frame);
  24. if (frame.contains(scrolledXInt, scrolledYInt)) {
  25. final float xc = scrolledXFloat - child.mLeft;
  26. final float yc = scrolledYFloat - child.mTop;
  27. ev.setLocation(xc, yc);
  28. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  29. if (child.dispatchTouchEvent(ev))  {
  30. mMotionTarget = child;
  31. return true;
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
  39. (action == MotionEvent.ACTION_CANCEL);
  40. if (isUpOrCancel) {
  41. mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
  42. }
  43. final View target = mMotionTarget;
  44. if (target == null) {
  45. ev.setLocation(xf, yf);
  46. if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  47. ev.setAction(MotionEvent.ACTION_CANCEL);
  48. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  49. }
  50. return super.dispatchTouchEvent(ev);
  51. }
  52. if (!disallowIntercept && onInterceptTouchEvent(ev)) {
  53. final float xc = scrolledXFloat - (float) target.mLeft;
  54. final float yc = scrolledYFloat - (float) target.mTop;
  55. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  56. ev.setAction(MotionEvent.ACTION_CANCEL);
  57. ev.setLocation(xc, yc);
  58. if (!target.dispatchTouchEvent(ev)) {
  59. }
  60. mMotionTarget = null;
  61. return true;
  62. }
  63. if (isUpOrCancel) {
  64. mMotionTarget = null;
  65. }
  66. final float xc = scrolledXFloat - (float) target.mLeft;
  67. final float yc = scrolledYFloat - (float) target.mTop;
  68. ev.setLocation(xc, yc);
  69. if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  70. ev.setAction(MotionEvent.ACTION_CANCEL);
  71. target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  72. mMotionTarget = null;
  73. }
  74. return target.dispatchTouchEvent(ev);
  75. }

  

  在第13行中,对onInterceptTouchEvent取反,因此如果在onInterceptTouchEvent中返回了false,会让表达式成立.

  结合之前的例子,在MyLayout重写了onInterceptTouchEvent,返回true,结果导致所有的按钮事件都被屏蔽.所以得出结论,按钮的点击处理事件,都是在13行中内部进行的.

  在上篇中,得出结论,调用View的dispatchTouchEvent方法都是有返回值的.如果一个控件是可以点击的,那么dispatchTouchEvent的返回值必定是true.

  因此如果按钮的点击事件得到执行,就会将MyLayout的Touch事件拦截掉.

  如果不是点击的按钮,而是空白区域,则31行一定不会返回true了,而是执行50行的return super.dispatchTouchEvent(ev);  这句代码会调用View中的dispatchTouchEvent方法.因为ViewGroup的父类就是View.因此MyLayout的Touch方法会得到执行.

  看一下整个ViewGroup事件分发过程的流程图:

  

  1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。

  2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

  3. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。

Android事件分发机制详解(2)----分析ViewGruop的事件分发的更多相关文章

  1. Android事件分发机制详解(1)----探究View的事件分发

    探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickLi ...

  2. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  3. Android事件分发机制详解

    事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...

  4. Android Touch事件传递机制详解 下

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...

  5. Android 的事件传递机制,详解

    Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...

  6. Android开发——事件分发机制详解

    0. 前言   转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...

  7. IOS 触摸事件分发机制详解

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...

  8. 【Android面试查漏补缺】之事件分发机制详解

    前言 查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?本文属于[Android面试查漏补缺]系列文章第一篇,持续更新中,感兴趣的朋友可以[关注+收藏]哦~ 本系列文章是对自己的前段时间面试经历的总结 ...

  9. Android View 事件分发机制详解

    想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...

随机推荐

  1. P1290 【欧几里德的游戏】

    P1290 [欧几里德的游戏] 真·做题全凭感性 从题目中很容易看出 这是一道\(Gcd\)的题 同时又结合了一些略略的博弈论(丢下锅跑真爽 我们看,辗转相减的\(a,b\)一共只有两种情况 \(a- ...

  2. js世界这么大,闭包想看看

    什么是闭包,为什么要用他?闭包是能够访问其他函数作用域的函数.我们来分析下句子成分(语文大神),闭包是函数,js函数的作用域分为全局作用域,局部作用域,eval作用域,并没有块级作用域形象的讲,每个函 ...

  3. [优化]Steamroller-freecodecamp算法题目

    晚上在medium看到一篇关于找工作的文章,里面提到一个面试题目--flattening an array(扁平化数组).这我好像在哪看过!应该是freecodecamp里的算法某一题.翻了下博客记录 ...

  4. 太阳地球月亮运行动画(使用@keyframes)

    闲来无事的demo <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  5. ABAP Table Control

    SAP中,Table Control是在Screen中用的最广泛的控件之一了,可以实现对多行数据的编辑.  简单来说,Table Control是一组屏幕元素在Screen上的重复出现,这就是它与普通 ...

  6. dataTables设置下拉滚动出现表头挤在一起的解决方法

    1.引入datatable的CSS文件 <link href="http://cdn.datatables.net/1.10.15/css/jquery.dataTables.min. ...

  7. percona-zabbix-templates插件安装监控MySQL

    前期准备:被监控机已经安装好php 1.在zabbix客户端安装mysql监控插件rpm包 rpm -ivh https://www.percona.com/downloads/percona-mon ...

  8. (转)在图像处理中,散度 div 具体的作用是什么?

    出处http://www.zhihu.com/question/24591127 按:今天看到这篇文章,有点感慨,散度这个概念我初次接触到至少应该是在1998年,时隔这么多年后看到这篇文章,真的 佩服 ...

  9. python中的文件操作小结2

    ''' #-----------文件修改---------- f=open("test_1",'r',encoding="utf-8") f2=open(&qu ...

  10. python 获取类中除内置方法外的所有方法名

    #!/usr/bin/env python# !-*- coding:utf-8 -*- class Menu: def __init__(self): pass def updateProject( ...