前言html
转载请注明,转自【http://www.javashuo.com/article/p-oqmfqqwl-e.html】谢谢!android
在自定义View中,常常须要处理Android事件分发的问题,尤为在有多个输入设备(如遥控、鼠标、游戏手柄等)时,事件处理问题尤其突出。Android事件分发机制,一直以来都是一个让众多开发者困扰的难点,至少笔者在工做的前几年中,没有特地研究它以前,就常常云里雾里。实际上,该问题的“七寸”就是dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)这三个方法和MotionEvent事件实体,我们这里索性称它们为“四大恶人”吧。本文将主要经过示例演示的方式来打这个“七寸”吧。程序员
本文的主要内容以下:编程
1、事件分发机制与生活场景ide
Android的事件分发机制和生活中的不少场景有着类似之处,可能Android的不少设计灵感就是来源于生活吧。函数
一、示例中的四个角色布局
在众多示例当中,有一个常常被拿来举例的经典场景就是PM(项目经理)、Team Leader、Programmer之间工做安排的问题,我们这里也用这个场景来类比,另外再加一个Boss的角色,以便于理解。如今这个公司中四个角色职位从高到低依次为,Boss > PM > Team Leader > Programmer。测试
二、可能出现的场景spa
通常来讲,一个寻常的工做流程是:Boss想到有个功能很流行,就会指示PM去办,PM会分发给Team Leader,而Team Leader会安排Proggrammer去编程实现。Boss是决策者,事件来源于他;PM和Team Leader是管理者,负责一层层把任务派发给本身的下属;Programmer是具体来作开发的,因此事件最后落在他的身上了,最后由他来完成。可是现实工做中,工做并不老是这个流程,还有不少其它场景,好比:设计
(1)市面上出现了一很火的行业,作智能手机。本公司是否须要也涉足这个行业,须要Boss本身开董事会来作决策。那么这个事情就是Boss应该处理的事情,他就不会派发给PM,Boss如下的员工看来,就跟没有任何事情同样。
(2)Boss肯定了智能手机是一个颇有前景的行业,肯定了要作,因而就召集PM,作好立项工做。那这个立项工做就是这个PM的工做了,如何立项,须要招聘什么样人等各项准备工做,PM就得本身作好整个计划,事情就到他这里为止了,不会再往下派发。
(3)作手机挣了钱,Boss决定奖励一些表现优异的Programmer。这须要先安排PM,PM而后安排下面的Team Leader对下面的Programmer们作综合考量,将优秀者报上去。这就是Team Leader须要完成的工做,他也没法再传下去。
(4)如前面说到的,Boss要求作一个市面上很流行的功能,通过PM和Team Leader层层派发到了Programmer手上。虽然事情派发到了Programmer手上,但也有两种情形:
1)在Programmer能力范围内,作得很完美。这种状况事情就在这里被处理掉了,无需再传递出去了。后续Boss一系列的相似功能也会继续派发下来,也以这样的流程来走下去。
2)Programmer能力有限,作不了。这种状况下,他就须要告诉Team Leader这一状况,把这个任务再依次传递给本身的领导。这样又有两种状况:①Team Leader 或者PM自己也是研发出身,开发能力也很强,就把这个开发任务给完成了,这样事情也就到此为止再也不传递了。在Boss看来,下面的团队有能力开发好这类需求,因而后续Boss一系列的相似功能也会继续派发下来。因为上一次的任务中,Team Leader或者PM知道本身的手下处理不了这类任务,因此当上一级把任务分发到本身这一层的时候,就不会再继续往下分发,而是本身亲自处完成。②Team Leader和PM都是管理出身,这个功能他们也不会作。因而就层层往上报,最后到老板那里,老板本身处理,是不了了之仍是再找招人,或者本身也是个大牛程序员本身能够开发出来,这个决策由老板来作。老板知道本身下面的团队完成不了这一系列任务,后续一系列这类功能,就再也不派发下去了。
......
上述列出了一些比较有表明性的可能状况,下面我们根据这些状况,来理解事件的分发机制。其实这里PM和Team Leader能够总体做为一个角色来看,只是为了后面看代码和日志方便对应,才分开为两个角色的。
2、MotionEvent简介
在讲Android事件分发机制前,先简单了解一些MotionEvent,由于它就是这个“事件”。如下截取了部分源码中的描述:
1 ...... 2 * <p> 3 * Motion events describe movements in terms of an action code and a set of axis values. 4 * The action code specifies the state change that occurred such as a pointer going 5 * down or up. The axis values describe the position and other movement properties. 6 * </p> 7 ...... 8 public final class MotionEvent extends InputEvent implements Parcelable { 9 public static final int ACTION_DOWN = 0; 10 public static final int ACTION_UP = 1; 11 public static final int ACTION_MOVE = 2; 12 ...... 13 }
MotionEvent,顾名思义,动做事件的意思。它经过一个action码和一套坐标值来描述动做。action码指定了当如指针按下或者抬起等事件发生时的状态改变,坐标值则描述了事件在屏幕中的位置和其它动做属性值。以下内容为MotionEvent的toString方法打印出来的结果:
MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=173.0, y[0]=138.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x2, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=34268429, downTime=34268429, deviceId=8, source=0x1002 }
从这里能够看到,事件发生的action,时间,坐标等不少信息。本文中暂时只关注aciton这个字段,“action=ACTION_DOWN”表示了按下事件。
平时触摸屏幕时,一个最简单的事件包括了“ACTION_DOWN”和“ACTION_UP”,“ACTION_DOWN”表示手指按下,而““ACTION_UP”表示手指抬起来,这两个action才构成了一个完整的事件。若是手指在屏幕上有移动,还会包含“ACTION_MOVE”,此时一个完整的事件就包括“ACTION_DOWN”,多个“ACTION_MOVE”,“ACTION_UP”。固然,实际工做中会有不少复杂的状况出现,可能会出现一些其它的aciton,本文为了演示的方便,只考虑“ACTION_DOWN”和“ACTION_UP”的场景。
3、示例代码
为了演示事件分发机制的工做流程,这里编写一个示例来进行演示。整个Acitivity模拟Boss角色;在其界面中的最外层模拟PM,继承自RelativeLayout,是一个父布局;PM下嵌套一层,也是一个父布局,继承自RelativeLayout,用于模拟Team Leader;最里面一层是一个叶子View,继承自Button,模拟Programmer。效果图及对应代码分别以下。
一、演示界面
二、默认场景下的代码示例
以下的代码中,须要重写的方法dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)均返回默认值,即super.xxx。平时我们使用系统原生控件时,没法修改它们的源码,因此系统给的默认场景就是这样的。
(1)Boss:EventDemoActivity
1 public class EventDemoActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_event_demo); 7 } 8 9 @Override 10 public boolean dispatchTouchEvent(MotionEvent ev) { 11 Log.i("songzheweiwang","[EventDemoActivity-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); 12 return super.dispatchTouchEvent(ev); 13 } 14 15 @Override 16 public boolean onTouchEvent(MotionEvent event) { 17 Log.i("songzheweiwang","[EventDemoActivity-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); 18 return super.onTouchEvent(event); 19 } 20 }
该Activity的布局文件为
1 //==========================activity_event_demo.xml=========================== 2 <?xml version="1.0" encoding="utf-8"?> 3 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 7 <com.example.demos.customviewdemo.ViewGroupOuter 8 android:layout_width="300dp" 9 android:layout_height="300dp" 10 android:layout_centerInParent="true" 11 android:background="@android:color/holo_orange_dark"> 12 13 <com.example.demos.customviewdemo.ViewGroupMiddle 14 android:layout_width="200dp" 15 android:layout_height="200dp" 16 android:layout_centerInParent="true" 17 android:background="@android:color/holo_blue_dark"> 18 19 <com.example.demos.customviewdemo.ViewInner 20 android:id="@+id/viewInner" 21 android:layout_width="100dp" 22 android:layout_height="100dp" 23 android:layout_centerInParent="true" 24 android:background="@android:color/holo_green_dark"/> 25 </com.example.demos.customviewdemo.ViewGroupMiddle> 26 </com.example.demos.customviewdemo.ViewGroupOuter> 27 </RelativeLayout>
(2)PM:ViewGroupOuter
1 public class ViewGroupOuter extends RelativeLayout { 2 3 public ViewGroupOuter(Context context, @Nullable AttributeSet attrs) { 4 super(context, attrs); 5 } 6 7 @Override 8 public boolean dispatchTouchEvent(MotionEvent ev) { 9 Log.i("songzheweiwang","[ViewGroupOuter-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); 10 return super.dispatchTouchEvent(ev); 11 } 12 13 @Override 14 public boolean onInterceptTouchEvent(MotionEvent ev) { 15 Log.i("songzheweiwang","[ViewGroupOuter-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); 16 return super.onInterceptTouchEvent(ev); 17 } 18 19 @Override 20 public boolean onTouchEvent(MotionEvent event) { 21 Log.i("songzheweiwang","[ViewGroupOuter-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); 22 return super.onTouchEvent(event); 23 } 24 }
(3)Team Leader:ViewGroupMiddle
1 public class ViewGroupMiddle extends RelativeLayout { 2 3 public ViewGroupMiddle(Context context, @Nullable AttributeSet attrs) { 4 super(context, attrs); 5 } 6 7 @Override 8 public boolean dispatchTouchEvent(MotionEvent ev) { 9 Log.i("songzheweiwang","[ViewGroupMiddle-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); 10 return super.dispatchTouchEvent(ev); 11 } 12 13 @Override 14 public boolean onInterceptTouchEvent(MotionEvent ev) { 15 Log.i("songzheweiwang","[ViewGroupMiddle-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); 16 return super.onInterceptTouchEvent(ev); 17 } 18 19 @Override 20 public boolean onTouchEvent(MotionEvent event) { 21 Log.i("songzheweiwang","[ViewGroupMiddle-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); 22 return super.onTouchEvent(event); 23 } 24 }
(4)Programmer:ViewInner
这里先以Button为例,由于Button默认是能够处理Touch事件的,也就是说,事件传到这里时,能被完美地处理掉。
1 @SuppressLint("AppCompatCustomView") 2 public class ViewInner extends Button{ 3 4 public ViewInner(Context context, AttributeSet attrs) { 5 super(context, attrs); 6 } 7 8 @Override 9 public boolean dispatchTouchEvent(MotionEvent event) { 10 Log.i("songzheweiwang","[ViewInner-->dispatchTouchEvent]event="+EventUtil.parseAction(event.getAction())); 11 return super.dispatchTouchEvent(event); 12 } 13 14 @Override 15 public boolean onTouchEvent(MotionEvent event) { 16 Log.i("songzheweiwang","[ViewInner-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); 17 return super.onTouchEvent(event); 18 } 19 }
(5)辅助类:
1 public class EventUtil { 2 public static String parseAction(int action) { 3 String actionName = "Unknow:action=" + action; 4 switch (action) { 5 case MotionEvent.ACTION_DOWN: 6 actionName = "ACTION_DOWN"; 7 break; 8 case MotionEvent.ACTION_MOVE: 9 actionName = "ACTION_MOVE"; 10 break; 11 case MotionEvent.ACTION_UP: 12 actionName = "ACTION_UP"; 13 break; 14 default: 15 break; 16 } 17 return actionName; 18 } 19 }
三、日志
点击上图中不一样的区域,会有不一样的结果。这里点击最中间的View,点击其余区域的结果及分析,咱们在后面再介绍。
1 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 7 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 8 06-07 13:35:23.524 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 9 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 10 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 11 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP 12 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_UP 13 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_UP 14 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_UP
四、结果分析
该事件包含了两action:ACTION_DOWN和ACTION_UP。前咱们说过,Programmer完美处理好了事件,本次流程就到这里为止了,再也不传递,Boss认为团队有能力处理这类任务,因此相似的任务也会一样会交给手下的团队,因此ACTION_UP也走了相似的流程,那么整个事件就算完成了,由Programmer完美完成。整个事件的序列图以下所示:
五、ViewInner没有能力处理的状况
上面的例子中,ViewInner是一个Button,它默认是有能力处理此次Touch事件的。可是若是这是一个默认没有能力处理该时间的控件,又会是一种怎样的情形呢?我们把ViewInner改成继承View再看看结果(仍然点击中间的ViewInner)。
1 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 7 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 8 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 9 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 10 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 12 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
此时,整个事件的流程就变成了如今这样,直观地表现为以下的流程图:
为何是这种结果呢?由于ViewInner继承自View,其默认状况下,是没有处理Touch事件的能力的。因此Programmer处理不了了,就一层一层网上报告,因为ViewGroupMiddle和ViewGroupOuter都继承自RelativeLayout,默认也是没有处理Touch事件的能力的,因此最后ACTION_DOWN事件就回到了Boss这里,由Boss本身来处理。Boss发现本身首先的团队没法处理这类事件,因此后面的ACTION_UP事件就本身处理了,而没有再往下派发了。这一点,和第二节中的第(4)点的第2)小点的状况②的场景是一致的。
若是在activity_event_demo.xml中为各个控件(包括父布局)加上属性[android:clickable="true"],或者在Activity中为对应控件添加监听点击事件,那么这个控件就有了处理Touch事件的能力了,就和以前使用Button的场景同样的。读者还能够试试在ViewInnner为View时,其父布局有处理Touch事件的能力时的场景(注意,要点击ViewInner来测试),那么这就是和第二节中的第(4)点的第2)小点的状况①的场景是一致的,这里我们再也不分析日志画流程图了。
4、Touch事件主要方法说明
一、Touch的三个主要方法概述
前面一直提到Touch事件的3个主要方法:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev),那么这三个方法的功能到底是什么呢?这里能够看看下面的表格的总结:
其实咱们能够从函数名称来大体判断其功能,dispatchTouchEvent,分发触摸事件,就是把事件传递下去,准确来讲就是是否要传递到子View以及本身的onInterceptTouchEvent方法和onTouchEvent方法,也就是说,不只管子Viiew,还管自身剩下的两个回调方法。onInterceptTouchEvent,事件拦截,它只管自身子View,而不会影响到自身后面两个方法的执行,若是拦截了,能够记忆为让本身的手下们无事可作。这两个方法容易混淆,须要重点理解和记忆。
在上述表格中还能够看到,Activity是没法回调onIntercepTouchEvent方法的,由于这个方法是ViewGroup中的方法,而Activity也不是View体系中,不是视图类,因此没有这个方法。咱们能够这样记忆,Activity是Boss,不是打工行列中的一员,本身的任务就是让下面的打工者没去作事情,全部该方法对他来讲,没有意义。叶子View也没有这个方法,由于本身没有子View了,也没有拦截的意义。
因为这三个方法都是boolean值,再加上默认情形下会返回super.xxx,这样,每个方法都会有三种可选值。我们这里先了解一下每一种取值会产生怎么样的结果。
二、事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
Touch事件发生时,Activity的dispatchTouchEvent方法会将事件传递给最外层控件的dispatchTouchEvent方法,并由该控件进行分发下去。从根元素依次往下传递,一直到最里面的叶子View,或者中途被某个控件终止,才结束这个派发过程。其分发逻辑以下:
(1)若是 return true,事件会分发到当前控件的dispatchTouchEvent方法中处理。同时事件中止往下分发,且当前控件的onInterceptTouchEvent和onTouchEvent都不会执行。
(2)若是 return false,事件中止往下派发,且当前控件的onInterceptTouchEvent和onTouchEvent也都不会执行。同时将事件返回给上一级的onTouchEvent事件,由上一级去决定处理仍是继续往上传递,本身不处理。
(3)返回默认的super.dispatchTouchEvent,事件会自动分发给当前View的onInterceptTouchEvent。
三、事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
在当前控件的dispatchTouchEvent方法返回默认的方式时,其拦截逻辑以下:
1)return true,表示将事件进行拦截,并将拦截到的事件交给当前控件的onTouchEvent来处理。
2)return false,表示将事件放行,事件会被传递到子View上,并由子View的dispatchTouchEvent方法继续派发。
3)return super.onInterceptTouchEvent(ev),和返回false的逻辑同样。
四、事件响应:public boolean onTouchEvent(MotionEvent ev)
(1)该方法会被执行的情形有以下两种:
1)子View没有处理事件,将事件返回来;
2)当前控件中dispatchTouchEvent返回默认的super.dispatchTouchEvent的状况下,且该控件的onInterceptTouchEvent返回false或者默认的super.onInterceptTouchEvent时。
(2)onTouchEvent方法响应逻辑以下:
1)返回true,当前事件会被处理掉。
2)返回false,当前事件不会被处理,返回给上一级的onTouchEvent方法来处理。
3)返回super.onTouchEvent,若是本身有能力处理该事件,则会处理,此时super.onTouchEvent的值为true;不然,若是本身没有能力处理该事件,则将事件返回到上一级中的onTouchEvent方法中处理,当前super.onTouchEvent的值为false。
5、Touch的3个主要方法返回值对事件分发影响的案例分析
上一节中介绍了Touch的3个主要方法的返回值下,对事件分发的处理逻辑。本节中,我们经过修改前面这三个方法中的返回值,来验证事件的分发流程(注意:如下状况下均点击中间的ViewInner控件)。
一、ViewGroupMiddle中dispatchTouchEvent返回true,其它均返回默认值时
1 06-07 19:15:53.220 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:15:53.222 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 6 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 7 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 8 06-07 19:15:53.238 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
参照第四节中的结论,ViewGroupMiddle的dispatchTouchEvent返回true,事件从Acitivty,通过ViewGroupOuter分发到ViewGroupMiddle中,在其dispatchTouchEvent方法中处理。ViewGroupMiddle的onInterceptTouchEvent和onTouchEvent均不会被调用,且事件也不会再往ViewInner中传递。既然事件是在ViewGroupMiddle的dispatchTouchEvent中被处理了,在Boss EventDemoActivity看来,本身手下的团队有能力处理这类事件,因此ACTION_UP也被派发下来,走一样的流程,直到全部事件处理完毕。
二、ViewGroupMiddle中dispatchTouchEvent返回false,其它均返回默认值时
1 06-07 19:31:50.093 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 6 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 7 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 8 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
参照第四节中的结论,ViewGroupMiddle的dispatchTouchEvent返回true,事件从Acitivty,通过ViewGroupOuter分发到ViewGroupMiddle中,且在dispatchTouchEvent方法中不处理此事件。ViewGroupMiddle的onInterceptTouchEvent和onTouchEvent均不会被调用,且事件也不会再往ViewInner中传递。本身处理不了事件,传递给上一级的onTouchEvent来处理,上一级也没能力处理,最后传给了EventDemoActivity的onTouchEvent。此时,在Boss看来,本身手下团队处理不了这类事件,因此后面的事件就再也不传递下去,都有本身来处理。
三、ViewGroupMiddle中onInterceptTouchEvent返回true,其它均返回默认值时
1 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 7 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 8 06-07 19:41:08.895 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 9 06-07 19:41:08.900 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 10 06-07 19:41:08.901 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
事件在ViewGroupMiddle中被拦截了,事件再也不派发到ViewInner中,而是交给本身的onTouchEvent来处理。前面说过,ViewGroupMiddle继承自RelativeLayout,默认是没有能力处理Touch事件的,因而就传递到上一级的onTouchEvent中,直到EventDemoActivity中的onTouchEvent方法。此时,在Boss看来,本身手下团队处理不了这类事件,因此后面的事件就再也不传递下去,都有本身来处理。
四、ViewGroupMiddle中onInterceptTouchEvent返回false,其它均返回默认值时
1 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 7 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 8 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 9 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 10 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 11 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 12 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
这种状况下,和使用默认super.onInterceptTouchEvent时是同样的,Log中中的日志也验证了这一点。事件派发流程在第三节中详细讲解过,这里就再也不赘述了。
五、ViewGroupMiddle中onTouchEvent为true,其它均返回默认值时
1 06-07 19:53:51.516 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 7 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 8 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 9 06-07 19:53:51.582 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 10 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 11 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 12 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP 13 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_UP
事件依次传递到ViewInner的onTouchEvent方法中,ViewInner默认没有能力处理该事件,传递到上一级ViewGroupMiddle中的onTouchEvent来处理。返回true表示被处理了,本次事件在此停止了。在Boss看来,手下团队有能力处理这类事件,因此后面的ACTION_UP事件仍然往下分发了。这里须要注意的是,ACTION_UP在ViewGroupMiddle的dispatchTouchEvent执行后直接进入到其onTouchEvent方法中了,没有通过onInterceptTouchEvent方法走,也没有往ViewInner中分发。这个场景就好像,经过ACTION_DOWN,ViewGroupMiddle已经知道本身的手下ViewInner处理不了这类任务,因此当同类任务从上面领导发放到本身这里的时候,就不用再继续往下分发,而是直接直接就处理掉了。
六、ViewGroupMiddle中onTouchEvent为false,其它均返回默认值时
1 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 5 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 6 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 7 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 8 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 9 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 10 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 11 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 12 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
这里在activity_event_demo.xml中使用ViewGroupMiddle时添加[android:clickable="true"],将ViewGroupMiddle设置为默承认以处理Touch事件。当设置为false值时,从日志来看,代表ViewGroupMiddle不管是否有能力,都确实没有处理事件,而是传给了上级。
7, 均为默认值时
当ViewGroupMiddle中onTouchEvent返回默认的super.onTouchEvent时,咱们在第三节中分析过ViewInner有能处理和没有能力处理两种状况下的事件处理逻辑,这里笔者再也不赘述。如今还有一个结论须要读者验证,就是都在返回默认super.xxx状况下,能够在ViewGroupMiddle中onTouchEvent方法中打印出super.onTouchEvent的值。能够发现,若是ViewGroupMiddle中onTouchEvent方法能够处理事件,则值为true,若是没有处理Touch事件的能力,则会返回false。这一点在第四节中讲过。
6、当触摸其它区域时分析
在前面分析打印log结果的时候,笔者都着重强调了要点击正中心的ViewInner。这是由于点击不一样的区域,会产生不一样的逻辑处理结果。那么点击区域和事件分发结果有什么样的关系呢?下面将第三节中的例子,3个主要方法都返回默认的super.xxx方法,由外到内依次点击Boss,PM,Team Leader,Programmer四个区域。获得了以下的log信息:
1 06-07 20:27:44.390 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 20:27:44.391 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 3 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 4 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 5 6 06-07 20:27:48.298 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 7 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 8 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 9 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 10 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 11 06-07 20:27:48.338 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 12 06-07 20:27:48.339 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 13 14 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 15 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 16 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 17 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 18 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 19 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 20 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 21 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 22 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 23 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 24 25 06-07 20:27:57.448 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 26 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 27 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 28 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 29 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 30 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 31 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 32 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 33 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 34 06-07 20:27:57.450 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 35 06-07 20:27:57.514 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 36 06-07 20:27:57.515 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
这四次触摸事件的日志结果用空格隔开,分析该log能够发现:当点击Boss区域时,里面的三个控件均未触发事件;当点击PM区域时,Team Leader和Programmer中的没有任何动做;点击Team Leader区域时,只有Programmer没有触发任何事件;当点击Programmer区域时,4个角色均被触发。那么这个结论就很显而易见了:当点击到View系统的某一层时,事件从外往内传递时,只到被点击的那一层为止,不会再派发到其子View中。这4个场景,是否是和咱们开篇第一节中提到的4种场景很类似呢?点击到哪一个区域,说明本来安排的任务自己就应该由该职位的人来完成,其手下就彻底能够当成是不存在的。
结语
到目前为止,Android的事件分发和传递机制就分析完了。本文中Touch事件的3个主要方法返回值均有3种情形,因此会有多种逻辑处理组合。这里选取了中间层ViewGroupMiddle来举例,只是做为表明来分析,笔者彻底能够经过其它的组合来分析更多的可能状况。若是分析中有不稳当或者不许确的地方,欢迎来拍砖。