Android的Touch事件分发机制简单探析
前言
Android中关于触摸事件的分发传递是一个很值得研究的东西。曾不见你引入了一个ListView的滑动功能,ListView就不听你手指的指唤来滚动了;也不知道为啥Button设置了onClick和onTouch,其中谁会先响应;或许你会问onTouch和onTouchEvent有什么区别,又该如何使用?这里一切的一切,只要你了解了事件分发机制,你会发现,解释这都不是事儿!
相关Touch事件的方法
1、public boolean dispatchTouchEvent(MotionEvent ev) ————事件分发方法,分发Event所调用
2、public boolean onInterceptTouchEvent(MotionEvent ev) ————事件拦截方法,拦截Event所调用
3、public boolean onTouchEvent(MotionEvent event) ————事件响应方法,处理Event所调用
拥有上述事件的类
1、Activity类(Activity及其各种继承子类)
dispatchTouchEvent()、onTouchEvent()
2、ViewGroup类(LinearLayout、FrameLayout、ListView等.....)
dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
3、View类(Button、TextView等.....)
dispatchTouchEvent()、onTouchEvent()
PS:需要特别注意一点就是ViewGroup中额外拥有onInterceptTouchEvent()方法,其他两个方法为这三种类所共同拥有。
方法的简单用途解析
我们可以发现这三个方法的返回值都为boolean类型,其实它们就是通过返回值来决定下一步的传递处理方向。
1、dispatchTouchEvent() ——用来分发事件所用
该方法会将根元素的事件自上而下依次分发到内层子元素中,直到被终止或者到达最里层元素,该方法也是采用一种隧道方式来分发。在其中会调用onInterceptTouchEvent()和onTouchEvent(),一般不会去重写。
返回false则不拦截继续往下分发,如果返回true则拦截住该事件不在向下层元素分发,在dispatchTouchEvent()方法中默认返回false。
2、onInterceptTouchEvent() ——用来拦截事件所用
该方法在ViewGroup源代码中实现就是返回false不拦截事件,Touch事件就会往下传递给其子View。
如果我们重写该方法并且将其返回true,该事件将会被拦截,并且被当前ViewGroup处理,调用该类的onTouchEvent()方法。
3、onTouchEvent() ——用来处理事件
返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View)
返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理
实战演练
好了,基础知识讲完了,上面的东西看不懂不要紧,现在要睁大眼睛好好看了,因为下面开始用例子讲解Touch的事件分发机制,相信能让大家更好的理解这个分发机制。
在这个例子中,我们需要重写四个类:
1、老板 ——> MyActivity
2、经理 ——> FrameLayout
3、组长 ——> LineaLayout
4、员工 ——> TextView
软件界面图如下:

【搞个通俗易懂的例子开开头】
1、按常理,领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。
2、另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。
流程演示
【1】、我们假设员工能力不足,也就是将TextView的onTouchEvent()方法设置返回false,表示其不能消费该事件。

事件传递的流程:

【2】、我们假设员工能处理该事件,也就是将TextView的onTouchEvent()方法设置返回true,表示其能处理该事件。

事件传递的流程:

【3】、我们假设员工和组长能力不足,即TextView和LinearLayout的onTouch()返回false,但是经理解决了该问题,即FrameLayout的onTouch()返回true

事件传递的流程:

【4】假设我们的组长在事件分发到他那里的时候,决定拦截下来不交给下面的员工,也就是onInterceptTouchEvent()返回为true,并且他也成功完成了任务,即onTouchEvent()返回值为true。

事件传递的流程:

做个小结
1、很明显,这些流程就是dispatchTouchEvent()的处理结果,但是前提是我们不去完全的重新实现这个方法,也就是保证需要return super.dispatchTouchEvent(ev);来确定父类的方法有被调用。而这些事件将会由上而下的逐层传递,直到传递到最底层的View元素,此时将会调用该View的onTouchEvent()方法来处理该事件;返回true来表示对该事件已经成功处理,如果返回false则并没有成功处理事件,将会把事件逐层向上传递,交给上层View的onTouchEvent()方法处理,以此类推,直至某一View成功处理该事件,或者到顶层View处理仍然返回false则放弃对该事件处理,事件消失。
2、如果在事件向下传递的过程中,被中途拦截,也就是View的onInterceptTouchEvent()方法返回true,那么该事件将停止向下传递,并交给该层的onTouchEvent()方法处理,无论处理成功与否,底层View将再也不会接收到该事件。PS:若处理失败,则会交由上层View的onTouchEvent()方法处理。
3、dispatchTouchEvent()具有记忆的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。
4、记忆功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。
附带代码
1、MyActivity
public class MyActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i("test", "【老板】下达任务:" + Util.actionToString(ev.getAction()) + ",找个人帮我完成,任务往下分发。");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean relust = false;
Log.i("test", "【老板】完成任务:" + Util.actionToString(event.getAction()) + ",【经理】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTaskTop(relust));
return relust;
}
}
2、MyFrameLayout:
public class MyFrameLayout extends FrameLayout {
public MyFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i("test", "【经理】下达任务:" + Util.actionToString(ev.getAction()) + ",找个人帮我完成,任务往下分发。");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean relust = false;
Log.i("test", "【经理】是否拦截任务:" + Util.actionToString(ev.getAction()) + ",拦下来?" + relust);
return relust;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean relust = true;
Log.i("test", "【经理】完成任务:" + Util.actionToString(event.getAction()) + ",【组长】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTask(relust));
return relust;
}
}
3、MyLinearLayout
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i("test", "【组长】下达任务:" + Util.actionToString(ev.getAction()) + ",找个人帮我完成,任务往下分发。");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean relust = true;
Log.i("test", "【组长】是否拦截任务:" + Util.actionToString(ev.getAction()) + ",拦下来?" + relust);
return relust;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean relust = true;
Log.i("test", "【组长】完成任务:" + Util.actionToString(event.getAction()) + ",【员工】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTask(relust));
return relust;
}
}
4、MyTextView
public class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i("test", "【员工】下达任务:" + Util.actionToString(event.getAction()) + ",我没手下了,唉~自己干吧");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean relust = false;
Log.i("test", "【员工】完成任务:" + Util.actionToString(event.getAction()) + ",【员工】现在只能靠自己了!是否解决:" + Util.canDoTask(relust));
return relust;
}
}
5、Util(工具类)
public class Util {
public static String actionToString(int action){
String result = null;
switch(action){
case MotionEvent.ACTION_DOWN:
result = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
result = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
result = "ACTION_UP";
break;
}
return result;
}
public static String canDoTask(boolean can){
String result = null;
if(can){
result = "完美解决该任务!";
}
else{
result = "这活搞不定,交给老大完成吧。";
}
return result;
}
public static String canDoTaskTop(boolean can){
String result = null;
if(can){
result = "完美解决该任务!";
}
else{
result = "这活搞不定,放弃该任务。";
}
return result;
}
}
作者:enjoy风铃
出处:http://www.cnblogs.com/net168/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则下次不给你转载了
Android的Touch事件分发机制简单探析的更多相关文章
- Android中的事件分发机制
Android中的事件分发机制 作者:丁明祥 邮箱:2780087178@qq.com 这篇文章这周之内尽量写完 参考资料: Android事件分发机制完全解析,带你从源码的角度彻底理解(上) And ...
- Android Touch事件分发机制学习
Android 事件分发机制 ViewGroup dispatchTouchEvent 返回true dispatchTouchEvent: Activity ACTION_DOWN Myrelat ...
- Android开发之Touch事件分发机制
原地址http://www.cnblogs.com/linjzong/p/4191891.html Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实 ...
- Android事件分发机制(二)30分钟弄明白Touch事件分发机制
Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理. View在 ...
- Android:30分钟弄明白Touch事件分发机制
Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理. View在 ...
- 【转】Android:Touch事件分发机制
Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理. View在 ...
- Android View的事件分发机制
准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...
- Android程序员事件分发机制学习笔记
通过问题来学习一个东西是很好的方法.学习Android中View的事件体系,我也通过给自己提问题,在解决问题的同时也就知道了其中原理. 首先来几个问题起步: 什么是事件?什么是事件分发机制? 在我们通 ...
- Android view 的事件分发机制
1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * ...
随机推荐
- Java15-java语法基础(十五)——内部类
java16-java语法基础(十五)内部类 一.内部类: 可以在一个类的内部定义另一个类,这种类称为内部类. 二.内部类分为两种类型: 1.静态内部类: 静态内部类是一个具有static修饰词的类, ...
- Linux系统性能监控工具:tsar 安装、配置、以及使用
介绍 tsar 是淘宝自己开发的一个监控工具,可用于收集和汇总系统信息,例如CPU,负载,IO和应用程序信息,例如nginx,HAProxy,Squid等.结果可以存储在本地磁盘或发送到Nagios. ...
- ----一个roadmap----
在课上了解到了学web前端三个基础,HTML.CSS.JS 从HTML开始,逐步学习CSS.JS 大致写了一个roadmap(应该是roadmap喔 就是这样,以后可能会来更错或者更新 对没错我来更错 ...
- turtle库的学习笔记
(1)turtle使用pen来绘制图形 pendown() 放下画笔,移动到指定点后继续绘制 penup() 提起画笔,用于另起一个地方绘制时使用 pensize(width) 设置画笔线条 ...
- P3258 [JLOI2014]松鼠的新家 (简单的点差分)
https://www.luogu.org/problemnew/show/P3258 注意开始和最后结尾 #include <bits/stdc++.h> #define read re ...
- 学习Acegi应用到实际项目中(1)
在此,本人声明,我处于菜鸟阶段,文章的内容大部分摘自zhanjia的博客(http://zhanjia.iteye.com/category/43399),旨在学习,有很多地方,我理解不够透彻,可能存 ...
- 在IDEA中配置spring boot项目的热更新
在我使用IDEA的过程中,我发现Spring Boot项目本来自带的一个热部署工具无法使用,这里在参考各家博客后给出解决方案: 修改POM 首先POM文件需要包含spring boot的热部署工具,m ...
- shell解析my.cnf配置文件
my.cnf配置格式如下 vi my.cnf[client]port=3306socket=/tmp/mysql.socket [mysqld]port=3306server-id=1datadir= ...
- xml to xsd ; xsd to xml
xml to xsd 工具网站 https://www.freeformatter.com/xsd-generator.html 示例xml <?xml version="1.0&qu ...
- Maven的多mirrors的配置
repo1 central Human Readable Name for this Mirror. http://repo1.maven.org/maven2/ repo2 central Huma ...