Touch 事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup 及其子类、Activity。方法与控件的对应关系如下表所示:
Touch 事件相关方法 | 方法功能 |
ViewGroup |
Activity |
public boolean dispatchTouchEvent(MotionEvent ev) | 事件分发 | Yes | Yes |
public boolean onInterceptTouchEvent(MotionEvent ev) | 事件拦截 | Yes | No |
public boolean onTouchEvent(MotionEvent ev) | 事件响应 | Yes | Yes |
从这张表中我们可以看到 ViewGroup 及其子类对与 Touch 事件相关的三个方法均能响应,而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。另外需要注意的是 View 对 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的响应的前提是可以向该 View 中添加子 View,如果当前的 View 已经是一个最小的单元 View(比如 TextView),那么就无法向这个最小 View 中添加子 View,也就无法向子 View 进行事件的分发和拦截,所以它没有 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev),只有 onTouchEvent(MotionEvent ev)。
一、Touch 事件分析
▐ 事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:
- 如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递;
- 如果 return false,事件分发分为两种情况:
- 如果当前 View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费;
- 如果当前 View 获取的事件来自外层父控件,则会将事件返回给父 View 的 onTouchEvent 进行消费。
- 如果返回系统默认的 super.dispatchTouchEvent(ev),事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。
▐ 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:
- 如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;
- 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;
- 如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。
▐ 事件响应:public boolean onTouchEvent(MotionEvent ev)
在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下:
- 如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。
- 如果返回了 true 则会接收并消费该事件。
- 如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。
到这里,与 Touch 事件相关的三个方法就分析完毕了。下面的内容会通过各种不同的的测试案例来验证上文中三个方法对事件的处理逻辑。
二、Touch 案例介绍
同样在开始进行案例分析之前,我需要说明测试案例的结构,因为所有的测试都是针对这一个案例来进行的,测试中只是通过修改每个控件中与 Touch 事件相关的三个方法的返回值来体现不同的情况。先来看张图:
上面的图为测试案例的布局文件 UI 显示效果,布局文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
<cn.sunzn.tevent.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#468AD7"
android:gravity="center"
android:orientation="vertical" > <cn.sunzn.tevent.TouchEventChilds
android:id="@+id/childs"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:background="#E1110D"
android:text="@string/hello" /> </cn.sunzn.tevent.TouchEventFather>
蓝色背景为一个自定义控件 TouchEventFather,该控件为外层 View,继承自 LinearLayout,实现代码如下:
package cn.sunzn.tevent; import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout; public class TouchEventFather extends LinearLayout { public TouchEventFather(Context context) {
super(context);
} public TouchEventFather(Context context, AttributeSet attrs) {
super(context, attrs);
} public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("sunzn", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
} public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("sunzn", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
} public boolean onTouchEvent(MotionEvent ev) {
Log.d("sunzn", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onTouchEvent(ev);
} }
红色背景为一个自定义控件 TouchEventChilds,该控件为内层 View,为 TouchEventFather 的子 View,同样继承自 LinearLayout,实现代码如下:
package cn.sunzn.tevent; import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout; public class TouchEventChilds extends LinearLayout { public TouchEventChilds(Context context) {
super(context);
} public TouchEventChilds(Context context, AttributeSet attrs) {
super(context, attrs);
} public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("sunzn", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
} public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("sunzn", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
} public boolean onTouchEvent(MotionEvent ev) {
Log.d("sunzn", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onTouchEvent(ev);
} }
接着实现 Activity 的代码,因为控件所有的事件都是通过 Activity 的 dispatchTouchEvent 进行分发的;除此之外还需要重写 Activity 的 onTouchEvent 方法,这是因为如果一个控件直接从 Activity 获取到事件,这个事件会首先被传递到控件的 dispatchTouchEvent 方法,如果这个方法 return false,事件会以冒泡方式返回给 Activity 的 onTouchEvent 进行消费。实现代码如下:
package cn.sunzn.tevent; import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent; public class TouchEventActivity extends Activity { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
} public boolean dispatchTouchEvent(MotionEvent ev) {
Log.w("sunzn", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
} public boolean onTouchEvent(MotionEvent event) {
Log.w("sunzn", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
} }
最后再附上 TouchEventUtil 的代码,TouchEventUtil 中并没有做多少事情,只是将以上 2 个自定义控件中各个方法的 MotionEvent 集中到一个工具类中并将其对应的动作以 String 形式返回,这样处理更便于实时观察控件的事件。代码如下:
package cn.sunzn.tevent; import android.view.MotionEvent; public class TouchEventUtil { public static String getTouchAction(int actionId) {
String actionName = "Unknow:id=" + actionId;
switch (actionId) {
case MotionEvent.ACTION_DOWN:
actionName = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
actionName = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
actionName = "ACTION_UP";
break;
case MotionEvent.ACTION_CANCEL:
actionName = "ACTION_CANCEL";
break;
case MotionEvent.ACTION_OUTSIDE:
actionName = "ACTION_OUTSIDE";
break;
}
return actionName;
} }
Touch 事件的分发和消费机制的更多相关文章
- Android事件分发机制(一) Touch 事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...
- Android 编程下 Touch 事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...
- Android 编程下Touch 事件的分发和消费机制
1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...
- 事件之Touch 事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...
- touch事件的分发和消费机制
Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...
- Android 编程下Touch 事件的分发和消费机制和OnTouchListener,OnClickListener和OnLongClickListener的关系
1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...
- 通俗理解Android事件分发与消费机制
深入:Android Touch事件传递机制全面解析(从WMS到View树) 通俗理解Android事件分发与消费机制 说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与Li ...
- View,ViewGroup的Touch事件的分发机制
原帖地址:http://blog.csdn.net/xiaanming/article/details/21696315 ViewGroup的事件分发机制 我们用手指去触摸Android手机屏幕,就会 ...
- Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制
转自:xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/21696315) 今天这篇文章主要分析的是Android的事件分发机制, ...
随机推荐
- Java-Java中System.arraycopy() 和 Arrays.copyOf()两者之间的区别
如果我们想拷贝一个数组,我们可能会使用System.arraycopy()或者Arrays.copyof()两种方式.在这里,我们将使用一个比较简单的示例来阐述两者之间的区别. 1.示例代码: Sys ...
- 谈“技术含量”的问题
最近又从离职同事那里听到这样的抱怨(原因),说做的事没有技术含量.想一想,从事车载软件开发这个行业快8年了,这个话题似乎从来没有停过.我自己曾经也为自己做的事是否有技术含量而苦恼过,今天就专门花点时间 ...
- mysql 多表联合更新
UPDATE mall_order moLEFT JOIN mall_order_goods mog ON mo.id = mog.order_idSET mo.ratio=0.08WHERE mog ...
- 通用js类库
/* 其它通用函数 */$(function() { // var General = function() { var _self = this; /* 写 cookie 操作 */ _self.S ...
- 如何更改firefox默认搜索引擎?一步搞定!
由于开发设计的需要,ytkah平时习惯使用firefox作为默认浏览器,火狐浏览器可添加的扩展功能比较,比如firebug.nofollow.seoquake等,还有比较友好的功能就是选中关键词拖动直 ...
- Goldbach's Conjecture
Goldbach's Conjecture Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I ...
- (11)UI布局和分辨率适配
一.Cocos编辑器 自动布局系统主要涉及固定与拉伸属性: 如图,总共可以修改控件的上下左右四个图钉和中间的两个拉伸条六个属性. 效果 1.当打开其中的任意一个图钉时,当前节点与父节点的对应边 ...
- Linux瑞士军刀:密码管理Keeweb
导读 如今,我们依赖于越来越多的线上服务.我们每注册一个线上服务,就要设置一个密码:如此,我们就不得不记住数以百计的密码.这样对于每个人来说,都很容易忘记密码.我将在本文中介绍 Keeweb,它是一款 ...
- ZeroMQ之Request/Response (Java)
自己最开始是在cloud foundry中接触过消息服务器(nats),或者说是消息中间件,也算是初步知道了一个消息服务器对于分布式的网络系统的重要性,后来自己也曾想过在一些项目中使用它,尤其是在一些 ...
- maven最齐全配置pom.xml
0001<project xmlns="http://maven.apache.org/POM/4.0.0"0002 0003xmlns:xsi="http://w ...