Android笔记:触摸事件的分析与总结----TouchEvent处理机制
其他相关博文:
Android笔记:触摸事件的分析与总结----MotionEvent对象
Android笔记:触摸事件的分析与总结----TouchEvent处理机制
Android中的事件类型分为按键事件和屏幕触摸事件。TouchEvent是屏幕触摸事件的基础事件,要深入了解屏幕触摸事件的处理机制,就必须掌握TouchEvent在整个触摸事件中的转移和处理过程。此处将对TouchEvent处理机制的学习做个小小的总结和备记。
当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是 ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?
这问题涉及到与每个View或者ViewGroup的子类都具有的三个和TouchEvent处理密切相关的方法:
1)dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
其中view类和Activity中都有dispatchTouchEvent()和onTouchEvent()两个方法。ViewGroup继承自View,而且还新添了一个onInterceptTouchEvent()方法。
这三个方法的返回值都是boolean值,对于返回结果,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。
一、dispatchTouchEvent
dispatchTouchEvent(MotionEventev) 这个方法用来分发TouchEvent,默认返回false。
先看下Activity中的注释和方法:
/**
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev)
{
if (ev.getAction() == MotionEvent.ACTION_DOWN)
{
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev))
{
return true;
}
return onTouchEvent(ev);
}
onInterceptTouchEvent()默认返回了false,注释的大意为重写该方法可以实现对触屏事件的拦截,使用该方法需要特别注意的是,该方法与View类的onTouchEvent(MotionEvent)或者View.onTouchEvent(MotionEvent)方法具有复杂的关联机制。结合onTouchEvent(),总结下onInterceptTouchEvent()大致的规则为:
1. down事件首先会传递到onInterceptTouchEvent()方法。
2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标View的onTouchEvent()处理。
3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
三、onTouchEvent
onTouchEvent()的处理机制详见此文:
Android笔记:触摸事件的分析与总结----MotionEvent对象
四、TouchEvent处理范例
此处创建一个包含自定义LinearLayout(ViewGroup类)和自定义TextView(View类)的Activity来分析触摸屏幕时TouchEvent的处理机制。
效果图如下:
<?xml version="1.0" encoding="utf-8"?>
<com.example.d_touchevent.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" >
<com.example.d_touchevent.MyTextView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/tv"
android:text="测试"
android:textSize="40sp"
android:textStyle="bold"
android:background="#F0F00F"
android:textColor="#0000FF"/>
</com.example.d_touchevent.MyLinearLayout>
MainActivity.java代码如下:
package com.example.d_touchevent; import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.MotionEvent; /**
* 参考资料:http://glblong.blog.51cto.com/3058613/1559320
* @author zeng
*
*/
public class MainActivity extends Activity
{
private String TAG = "Activity --- "; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} @Override
public boolean onTouchEvent(MotionEvent event)
{
boolean b = super.onTouchEvent(event); int action = event.getAction(); switch (action)
{ case MotionEvent.ACTION_DOWN:
Log.e(TAG , "ACTION_DOWN --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "onTouchEvent 处理 --- " + b); break; } return b;
} @Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
int action = ev.getAction(); switch (action)
{ case MotionEvent.ACTION_DOWN:
Log.e(TAG, "ACTION_DOWN --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "dispatchTouchEvent 分发 --- "); break; }
return super.dispatchTouchEvent(ev);
} }
MyLinearLayout.java代码如下:
package com.example.d_touchevent; import android.widget.LinearLayout;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent; /**
* 参考资料:http://glblong.blog.51cto.com/3058613/1559320
* @author zeng
*
*/
public class MyLinearLayout extends LinearLayout
{
private final String TAG = "L布局 --- "; public MyLinearLayout(Context context, AttributeSet attrs)
{
super(context, attrs); Log.e(TAG, TAG);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
int action = ev.getAction(); switch (action)
{ case MotionEvent.ACTION_DOWN:
Log.e("", " ");
Log.e("", " ");
Log.e(TAG, "ACTION_DOWN --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "dispatchTouchEvent 分发 --- "); break; }
return super.dispatchTouchEvent(ev);
} @Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
boolean b = false; int action = ev.getAction();
switch (action)
{ case MotionEvent.ACTION_DOWN: Log.e(TAG, "ACTION_DOWN --- " + TAG + "onInterceptTouchEvent 拦截 --- " + b); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "onInterceptTouchEvent 拦截 --- " + b); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "onInterceptTouchEvent 拦截 --- " + b); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "onInterceptTouchEvent 拦截 --- " + b); break; } return b; } @Override
public boolean onTouchEvent(MotionEvent ev)
{
boolean b = true; int action = ev.getAction();
switch (action)
{ case MotionEvent.ACTION_DOWN: Log.e(TAG, "ACTION_DOWN --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "onTouchEvent 处理 --- " + b); break; } return b;
}
}
MyTextView.java代码如下:
package com.example.d_touchevent; import android.widget.TextView;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent; /**
* 参考资料:http://glblong.blog.51cto.com/3058613/1559320
* @author zeng
*
*/
public class MyTextView extends TextView
{
private final String TAG = "TextView --- "; public MyTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
int action = ev.getAction(); switch (action)
{ case MotionEvent.ACTION_DOWN: Log.e(TAG, "ACTION_DOWN --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "dispatchTouchEvent 分发 --- "); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "dispatchTouchEvent 分发 --- "); break; }
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent ev)
{
boolean b = false; int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "ACTION_DOWN --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP --- " + TAG + "onTouchEvent 处理 --- " + b); break; case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_CANCEL --- " + TAG + "onTouchEvent 处理 --- " + b); break; } return b; } }
五、范例运行分析
注:a.以下Logcat中,若没有特别说明,dispatchTouchEvent()都按默认方法返回false。
b.为方便,L布局简写为L,TextView简写为T,Activity简写为A,下同。
1)点击范例中的【测试】按钮,运行日志如下:
结论:
当ACTION_DOWN事件产生时,首先触发了Activity的dispatchTouchEvent()方法;接着传递到ViewGroup上,触发L布局的dispatchTouchEvent()方法继续分发TouchEvent;L布局的onInterceptTouchEvent()方法为false,即不会拦截TouchEvent的传递,因而继续传递到ViewGroup里的View对象TextView中,此时仍然先是调用了TextView的dispatchTouchEvent()方法来处理TouchEvent的分发。从上到下依次传递:Activity -> L布局 -> TextView。
同理,当ACTION_UP事件产生时,首先也是Activity的dispatchTouchEvent()方法,接着再到L布局的dispatchTouchEvent()方法。
2)L.dispatchTouchEvent() = true ,运行日志如下:
结论:
此时,每个触摸事件产生时,都只执行到L布局的dispatchTouchEvent()方法,而不会继续再传递并触发其他方法。
3)A.dispatchTouchEvent() = false && L.dispatchTouchEvent() = false && T.dispatchTouchEvent() = true,运行日志如下:
结论:
由上可见,当TouchEvent由Activity传递到TextView时,执行到dispatchTouchEvent()后便结束了。也就是到TextView时,Android系统认为ACTION_DOWN和ACITON_UP都已经被消费了,而没有继续分发下去。
4)L.onInterceptTouchEvent = true && L.onTouchEvent = true ,运行日志如下:
结论:
这种情况下,L布局处理了所有的TouchEvent。
5)L.onInterceptTouchEvent = true && L.onTouchEvent = false , 运行日志如下:
结论:
L布局只处理了ACTION_DOWN事件,而L布局最外层的ctivity处理了TouchEvent。
6)L.onInterceptTouchEvent=false && L.onTouchEvent=true && T.onTouchEvent=true , 运行日志如下:
结论:
TouchEvent完全由TextView处理。
7)L.onInterceptTouchEvent=false && L.onTouchEvent=true && T.onTouchEvent=false , 运行日志如下:
结论:
TextView只处理了ACTION_DOWN事件,LinearLayout处理了所有的TouchEvent。
六、分析总结
1.三个主要相关的方法的默认值
所有dispatchTouchEvent方法的默认值都是false。
ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.
Activity和ViewGroup里的onTouchEvent默认值都是false。
View里的onTouchEvent返回默认值是true.这样才能执行多次touch事件。
2.TouchEvent的处理流程
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则表示该触摸事件已经被消费了,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“ 消失”,而且接收不到下一次事件。
3.TouchEvent的处理流程图
自己制作了个TouchEvent处理的流程图,方便理清TouchEvent事件在各种UI对象以及对应方法中的处理机制。将流程图与上面的运行日志结合分析,发现对TouchEvent处理的机制清晰了很多。若有错误之处,欢迎指教。
Android笔记:触摸事件的分析与总结----TouchEvent处理机制的更多相关文章
- 一个demo让你彻底理解Android中触摸事件的分发
注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...
- Android学习笔记触摸事件
案例代码: activity_main.xml <?xml version="1.0" encoding="utf-8"?> <Relativ ...
- iOS学习笔记--触摸事件
最近空闲时间在学习iOS相关知识,几周没有更新文章了,今天总结下这些天的学习内容,也整理下iOS的学习笔记,以便以后查阅翻看- iOS中的事件可以分为3大类型: 触摸事件 加速计事件 远程控制事件 响 ...
- Android 手势&触摸事件
在刚开始学Android的时候,就觉得Google的文档不咋样,在研究手势时,更加的感觉Google的文档写得实在是太差了.很多常量,属性和方法,居然连个描述都没有. 没有描述也就罢了,但是OnGes ...
- Android笔记之——事件的发生(2)
Java: package com.example.helloworld;import android.os.Bundle;import android.view.KeyEvent;import an ...
- Android笔记之——事件的发生
Java:package com.example.helloworld;import android.content.Intent;import android.support.v7.app.AppC ...
- 代码笔记-触摸事件插件hammer.js使用
如果要使用jquery,则需要下载jquery.hammer.min.js版本 新建一个hammer对象生成的对象是dom对象,不能直接使用jqeury 的 $(this)方法,需要先将其转成jqu ...
- Android笔记(二十五) ListView的缓存机制与BaseAdapter
之前接触了ListView和Adapter,Adapter将数据源和View连接起来,实际应用中,我们要显示的数据往往有很多,而屏幕只有那么大,系统只能屏幕所能显示的内容,当我们滑动屏幕,会将旧的内容 ...
- 图解Android触摸事件分发
Android中触摸事件传递过程中最重要的是dispatchTouchEvent().onInterceptTouchEvent()和onTouchEvent()方法. View和Activity有d ...
随机推荐
- memcache分布式实现、memcache分布…
Memcache的分布式介绍 memcached虽然称为"分布式"缓存服务器,但服务器端并没有"分布式"功能.服务器端仅包括内存存储功能,其实现非常简单.至于m ...
- 31、三层架构、AJAX+FormsAuthentication实现登陆
三层架构 前段时间公司要求修改一个网站,打开后我疯了,一层没有都是调用的DB接口,遍地的SQL语句,非常杂乱. 什么是三层架构? 三层架构是将整个项目划分为三个层次:表现层.业务逻辑层.数据访问层.目 ...
- html 中设置span的width完美解决方法
在默认情况下,由于span是行标签,设置width是无效的.只有改变display的属性,才可以实现设置宽度. 1.初步想法 span{ background-color:#ccc; display: ...
- 一行代码实现headView弹簧拉伸效果
前言 很多app的个人中心上部的headView都实现了弹簧拉伸的效果,即tableView的top并不随着下拉而滑动,而是紧紧的停在屏幕的最上方. 我们今天就分析一下这个效果的实现方式. 分析 关键 ...
- WCF 无法生成 client
在MVC中调用WCF 总是没有client 后来在网上查找原因,去掉Reuse type in referrenced assenbiles ,就可以生成代理代码.
- ios专题 - APP设计流程
网上看到这篇文章,觉得基本的flow很有帮助,转过来收藏了:作者:关于Sarah Parmenter英国艾塞克斯(英国英格兰东南部的郡)Youknowwho设计工作室的创始人,Sarah Parmen ...
- 网页icon和文本对齐神技 2016.03.23
一直以来icon和文本需要对齐都使用vertical-align: middle;的方法,但兼容性不理想.参考了鑫旭大大的博客,终于收获不用vertical-align可以对齐的神技,原博点这里. 代 ...
- Codevs 1010 过河卒 2002年NOIP全国联赛普及组
1010 过河卒 2002年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 传送门 题目描述 Description 如图,A 点有一个过河卒 ...
- OpenCV Tricks[OpenCV 笔记3]
官方例程 事例程序位于opencv-3.1.0/samples/cpp/ 目录下,可以通过编译整个工程,编译所有的Sample Code 显示当前使用的OpenCV版本 CV_VERSION为标识当前 ...
- 从ZOJ2114(Transportation Network)到Link-cut-tree(LCT)
[热烈庆祝ZOJ回归] [首先声明:LCT≠动态树,前者是一种数据结构,而后者是一类问题,即:LCT—解决—>动态树] Link-cut-tree(下文统称LCT)是一种强大的数据结构,不仅可以 ...