Android 的事件传递机制,详解
Android 的事件传递机制,详解
前两天和一个朋友聊天的时候。然后说到事件传递机制。然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的非常模糊。不过知道事件是从外层先传到内层,在从内存最后回馈到外层,可是详细的几个方法的调用过程。自己却知道的不是非常详细,我想非常多人都是这样的情况,然后自己就上网去查,然后看到的全部都是在讲会调用哪个几个方法。可是基本没有讲这几个方法的详细作用。
自己回去写了demo 。今天就把自己的理解写出来。希望大家能更详细了解这几个方法的详细作用。
首先事件传递会用到的几个方法,我们先列举一下。
- public boolean onInterceptTouchEvent(MotionEvent ev)
- public boolean dispatchTouchEvent(MotionEvent ev)
- public boolean onTouchEvent(MotionEvent ev)
我们查看源代码,能够看到,onInterceptTouchEvent 这种方法是viewGroup 的方法
可是 dispatchTouchEvent 和 onTouchEvent 都是view 方法。
那么我们先写一个View OutView(外层view)继承ViewGroup ,MiddleView(中层View) 继承ViewGroup ,InnerView (内层Button,Button 也是继承的view )继承View
既然 dispatchTouchEvent 和 onTouchEvent 是view 的方法,那么我们在每一个view 里面重写这两个方法。例如以下。
每一个方法会打印两边log,第一个log,是调用这种方法開始的时候,就打一句,第二个log ,是这种方法调用父类,也就说,该走的逻辑走完,而且有返回值的时候。为了log 清楚。都不过打印了 ACTION_DOWN 事件的。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent");
break;
}
boolean flag = super.dispatchTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent:" + flag);
break;
}
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent");
break;
}
boolean flag = super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent:" + flag);
break;
}
return flag;
}
输出的log 例如以下
toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:true
toucheventdemo E/OutView﹕ dispatchTouchEvent:true
能够看到前三个尽管都是直接都是dispatchTouchEvent 方法里面的log 打印的,可是log并没有返回值,也就是说,都是运行的方法里面第一句log。
可是当运行到InnerView﹕ dispatchTouchEvent 的时候。又运行了InnerView﹕ onTouchEvent 的方法
事实上是这样的, boolean flag = super.dispatchTouchEvent; 这一句,super 中,运行的dispatchTouchEvent 。
事实上会做两件事
1。第一件事。是看当前的view 有没有子view,假设有子view,而且会推断当前的触摸点是否在子view 上面。假设在的话。那么就会调用子view的dispatchTouchEvent ,然后一层一层往里面调用,
2.一直调用到没有子view 的时候。然后停止dispatchTouchEvent ,由于InnerView 是继承的view,当然没有子view,然后这个时候,就開始调用子view 的onTouchEvent。
onTouchEvent 返回 true 。那么当前view 的dispatchTouchEvent 也会返回view, 此时,就会讲dispatchTouchEvent 的分发结果反馈给父view,当前view 的父view 的dispatchTouchEvent 也会返回true,以此类推,返回到最外层。
那么假设里层的view 返回的true 那假设最里层的view onTouchEvent返回的是false 那?我们将里层的view 继承改成View,
toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent:false
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false
我们会发现,当里层的view ,onTouchEvent:false 的时候。那么里层的dispatchTouchEvent 也是false ,而且此时立刻会调用。里层view 的父view(也就是MiddleView) 的 onTouchEvent 事件,MiddleView的onTouchEvent 也返回false ,那么此时MiddleView dispatchTouchEvent 也会是false,然后接着调用了最外层 OutView onTouchEvent 方法。
从这两个小试验,你能看出,dispatchTouchEvent 的返回结果是怎么得来的吗?
分两种情况
当前 没有子view 那么当前view 的 dispatchTouchEvent 结果就是当前view onTouchEvent 的结果
当前view 有子view, 此时又分两种情况
- 子view 的 dispatchTouchEvent 返回false 那么当前view 的 dispatchTouchEvent 结果參照第一种情况,也就是当前view onTouchEvent 运行 ,而且dispatchTouchEvent 的结果是 当前view 的 onTouchEvent 的结果
- 子view 的 onTouchEvent 返回true 。那么 此时当前view 的onTouchEvent 的不运行 直接直接返回的是子view 的 dispatchTouchEvent 返回的view
将两种情况再总结一下。就是。当前view的 dispatchTouchEvent 的返回结果是:假设子view 的 dispatchTouchEvent 返回true ,那么当前view 不运行onTouchEvent ,当前view 的 dispatchTouchEvent 直接返回true, 假设子view 的dispatchTouchEvent 返回false ,当前view 的 dispatchTouchEvent的结果看 看当前view 的onTouchEvent 结果。
看到这里,你是否对。 dispatchTouchEvent onTouchEvent 略微有一些理解了那?事实上我们看方法的名称也应该能理解一些,dispatchTouchEvent 翻译:触摸事件分发, onTouchEvent 触摸事件,也就是说dispatchTouchEvent 主要做的是处理的触摸事件的分发管理,用来告诉父view 当前view 有没有处理我们的触摸事件。而且处理,当前触摸的点,还有没有子view, onTouchEvent 用来处理我们详细的触摸事件。
(详细这里为什么说还要推断当前触摸的点还有没有子view。你能够自己试验,触摸MiddleView 之内。InnerView之外的区域。看会不会打印InnerView 里面的log )
说了半天,我们一直在说dispatchTouchEvent onTouchEvent 这两个方法。那么onInterceptTouchEvent 这种方法又是干嘛的那?
首先翻译api onInterceptTouchEvent : 拦截TouchEvent 事件。
应为onInterceptTouchEvent 是viewGroup 的方法,那么我们重写OutView 和 MiddleView 的onInterceptTouchEvent方法
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent");
break;
}
boolean flag = super.onInterceptTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent:" + flag);
break;
}
return flag;
}
然后运行。点击InnerView 看log
toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent:false
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false
我们会发现,在全部的详细运行 dispatchTouchEvent 的方法一開始运行。都会先运行当前view 的onInterceptTouchEvent 结果。onInterceptTouchEvent 的默认返回值是false
如今我们改动一下MiddleView 的onInterceptTouchEvent 返回值为true 看代码
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent");
break;
}
boolean flag = super.onInterceptTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent:" + true);
break;
}
return true;
}
然后我们运行,看log
toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent:true
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false
我们会发现,当改动了,MiddleView 的onInterceptTouchEvent 返回结果是true 之后,
本来应该向InnerView 做分发的那部分log 没有了。然后直接运行了当前MiddleView 的 onTouchEvent 结果,然后运行完
onTouchEvent结果,之后,当前view 的dispatchTouchEvent 的结果也出来了,然后紧接着把这个有反馈给了OutView 外层view 的。而且运行的原理和我们前面只重写 dispatchTouchEvent onTouchEvent 的原理一样。
到这里你也应该知道这个onInterceptTouchEvent 是干嘛的吧,他事实上就是拦截事件是否要继续往下传递。假设返回true ,那么代表 事件被当前view 拦截,而且不向下传递,直接运行当前view 的 onTouchEvent 事件。
到这里,我想应该清楚这几个方法的含义了吧。
onInterceptTouchEvent : 拦截事件。推断是否向下传递。
返回true 代表拦截。返回false 代表不拦截
dispatchTouchEvent : 事件分发,管理事件的分发,向子view 分发事件,而且,依据得到的子view的事件处理情况,来推断是否响应当前view的 触摸事件 返回true,代表当前view 的或者子view 已经处理事件,告诉父view, 不须要处理事件,返回false ,代表当前view 或者子view 也没有处理事件,告诉父view,能够去处理事件了。
onTouchEvent : 事件的处理,在此方法内处理事件。
返回true ,代表当前view 处理了事件,false 代表当前view 没有处理事件。
然后我们再看另外一个可能经常使用的方法。
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
首先翻译: 请求禁止拦截触摸事件 ,字面理解,就是禁止吊我们的拦截。也就是禁止吊这种方法。onInterceptTouchEvent
那么我们如今把全部的onInterceptTouchEvent dispatchTouchEvent onTouchEvent 都加上move 事件。
代码例如以下,
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
break;
}
boolean flag = super.onInterceptTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN:" + flag);
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE:" + flag);
break;
}
return flag;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
}
boolean flag = super.dispatchTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN:" + flag);
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE:" + flag);
break;
}
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE" );
break;
}
boolean flag = super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN:" + flag);
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE:" + flag);
break;
}
return flag;
}
而且给 MiddleView 的onInterceptTouchEvent 方法中加上 requestDisallowInterceptTouchEvent(true);
例如以下
public boolean onInterceptTouchEvent(MotionEvent ev) {
//加上此句
requestDisallowInterceptTouchEvent(true);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
break;
}
boolean flag = super.onInterceptTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN:" + flag);
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE:" + flag);
break;
}
return flag;
}
那么如今我们触摸,然后看log ,可能log 比較多。可是希望大家慢慢细致看。
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/OutView﹕ onInterceptTouchEvent ACTION_DOWN
toucheventdemo E/OutView﹕ onInterceptTouchEvent ACTION_DOWN:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent ACTION_DOWN
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent ACTION_DOWN:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_DOWN
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_DOWN:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_DOWN:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_DOWN:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_DOWN:true
//从这里開始move事件。
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE:true
我们能够看处理啊,第一次是ACTION_DOWN 事件的时候一切正常,(InnerView 是继承的 Button,所以onTouchEvent 返回的是true)
可是从ACTION_MOVE 你会发现,全部的onInterceptTouchEvent 事件全部没有了。事实上,也就这种方法也就是这种方法起的作用,
好了。全部的东西讲完了,不知道你如今是否对事件传递有足够的理解那?
Android 的事件传递机制,详解的更多相关文章
- Android Touch事件传递机制详解 下
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...
- Android Touch事件传递机制详解 上
最近总是遇到关于Android Touch事件的问题,如:滑动冲突的问题,以前也花时间学习过Android Touch事件的传递机制,可以每次用起来的时候总是忘记了,索性自己总结一下写篇文章避免以后忘 ...
- Android Touch事件传递机制详解
Android开发的朋友经常处理各种触摸事件,然而在触摸事件的传递过程中主要用到三个方法:dispatchTouchEvent().onInterceptTouchEvent()和onTouchEve ...
- Android事件传递机制详解及最新源码分析——ViewGroup篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...
- Android事件传递机制详解及最新源码分析——View篇
摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于安卓事件传递机制相信绝大部分开发者都听说过或者了解过,也是面试中最常问的问题之一.但是真正能从源码角度理解具体事件传递流程的相信并不多, ...
- Android事件传递机制详解及最新源码分析——Activity篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...
- Android开发——事件分发机制详解
0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...
- Android View 事件分发机制详解
想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...
- Android webkit 事件传递流程详解
前言:基于android webview 上定制自己使用的可移植浏览器apk,遇到好多按键处理的问题.所以索性研究了一下keyevent 事件的传递流程. frameworks 层 keyevent ...
随机推荐
- Qt线程外使用Sleep
一:方法1 QTime t; t.start(); while(t.elapsed()<1000){ QCoreApplication::processEvents();} 二:方法2 ...
- 使用HAproxy如何实现web站点的动静分离
HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案. HAProxy特别 适用于那些负载特大的web站点,这些站点通常 ...
- 华为S5300系列升级固件S5300SI-V100R006C00SPC800.cc
这个固件附带了web,V100R005可以直接升级到这个版本,但没什么必要,因为V100R005本身可以直接升级到V200. 升级小插曲: 1.升级的使用使用Windows,不要用Mac或者Linux ...
- hybrid App h5二级页面返回的时候保持与一级页面浏览的位置一致
最近在开发公司hybrid app的时候,需要将原本原生的配置中心模块统一变更为H5,做完之后从测试那里反馈回来这样一个问题,当滑到页面底部或中部的时候进入子页面进行设置,返回的时候页面应该定位到离开 ...
- unity3d如何快速接入渠道SDK之Unity篇
原文链接: http://bbs.tianya.cn/post-414-53320-1.shtml 首先我们讲一下,为什么要介绍这个插件? 是因为这个插件极大的简化了我对接渠道SDK的工作量,精力和时 ...
- Tomcat6内存不足问题及解决方法
1.Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,有可能导致系统无法运行.常见的问题是报Tomcat内存溢出错误,Out of Memory(系统内存不足)的异常 ...
- coursera课程Text Retrieval and Search Engines之Week 1 Overview
Week 1 OverviewHelp Center Week 1 On this page: Instructional Activities Time Goals and Objectives K ...
- 【BZOJ】【3437】小P的牧场
DP/斜率优化 斜率优化基本题……等等,好像就没啥变化啊= = 嗯目测这题跟仓库建设差不多?写题的时候倒是没想这么多……直接推了公式. $$f[i]=min\{f[j]+cal(j,i)+a[i]\} ...
- 【六】注入框架RoboGuice使用:(Singletons And ContextSingletons)
上一篇我们简单的介绍了一下RoboGuice的使用([五]注入框架RoboGuice使用:(Your First POJO Injection)),今天我们来看下单例以及上下文单例(ContextSi ...
- Kmeans聚类算法分析(转帖)
原帖地址:http://www.opencvchina.com/thread-749-1-1.html k-means是一种聚类算法,这种算法是依赖于点的邻域来决定哪些点应该分在一个组中. ...