Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup、View、Activity。继承ViewGroup的大多是容器控件,如LinearLayout等,而继承View的大部分是显示控件比如TextView,ImageView等(当然,ViewGroup本身是继承View的),显示控件没有onInterceptTouchEvent。

来看一些例子。

情形1

只有activity,没有子控件:
代码如下:
package com.example.toucheventdemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
public class MainActivity extends Activity
{
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onTouchEvent--ACTION_UP");
break;
}
return super.onTouchEvent(event);
}
}

日志信息:

可以看到:总是先执行dispatchTouchEvent,再执行onTouchEvent.。

情形2:

将上面代码dispatchTouchEvent的返回值改为true。
日志:
 
可以看到,只执行了dispatchTouchEvent,而没有执行onTouchEvent。说明在activity中dispatchTouchEvent先于onTouchEvent执行,如果将dispatchTouchEvent返回值置为true,表示事件被消费了,不再传递。
 
 
 
 情形3:
 加入一个子控件
 以自定义Button为例:
package com.example.toucheventdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
public class MyButton extends Button
{
private static final String TAG = "MyButton";
public MyButton(Context context)
{
super(context);
}
public MyButton(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onTouchEvent--ACTION_UP");
break;
}
return super.onTouchEvent(event);
}
}

mainActivity代码如下:

package com.example.toucheventdemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends Activity
{
private static final String TAG = "MainActivity";
private MyButton but = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); but = (MyButton) findViewById(R.id.but);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onTouchEvent--ACTION_UP");
break;
}
return super.onTouchEvent(event);
} }

此时点击Button按钮,查看日志:

执行流程是首先由activity捕获到ACTION_DWON事件,然后调用activity的dispatchTouchEvent,接着绕开activity的onTouchEvent直接将事件传递给子控件,调用MyButton的dispatchTouchEvent,在之后调用该控件的onTouchEvent,ACTION_UP事件也是一样的流程。

情形4:

跟情形2类似,将情形3的activity的DispatchTouchEvent的返回值改为true,点击按钮,很显然,touch事件将不会被分发给Button,所以点击按钮日志是这样的:

日志信息:

情形5:

将情形3的myButton的DispatchTouchEvent的返回值改为true,点击按钮,很显然,当touch事件传递到button时,先被dispatchTouchEvent捕获,由于返回true,所以事件被消费,便不往下面传递,所以Button的onTouchEvent方法不被调用。

日志信息:

 
 
情形6:
将情形3的myButton的onTouchEvent的返回值改为false。
日志:
touch事件传到button时,因为onTouchEvent返回值为false,所以继续交由activity的onTouchEvent方法去执行,紧接着,ACTION_UP的动作将不再传递给button,直接由activity捕获了。
 
情形7:
给Activity的button按钮增加onTouchListener和onClickListener
package com.example.toucheventdemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
public class MainActivity extends Activity implements OnClickListener,OnTouchListener
{
private static final String TAG = "MainActivity";
private MyButton but = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); but = (MyButton) findViewById(R.id.but);
but.setOnClickListener(this);
but.setOnTouchListener(this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onTouchEvent--ACTION_UP");
}
return super.onTouchEvent(event);
}
@Override
public void onClick(View v)
{
Log.i("MyButton","ONCLICK");
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i("MyButton","onTouch--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i("MyButton","onTouch--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i("MyButton","onTouch--ACTION_UP");
break;
}
return false;
}
}

现在点击按钮日志打印如下信息:

首先touch事件由activity捕获,调用activity的dispatchTouchEvent,紧接着调用Button的dispatchTouchEvent继续分发touch事件,接着并没有调用button的onTouchEvent,而是先调用了onTouch方法,这是因为button按钮注册了onTouchListener的缘故,待onTouch事件处理完之后,由于返回值为false,所以touch事件传递给了button的onTouchEvent。接着ACTION_UP事件也是类似的过程,但当Button的onTouchEvent结束后,还调用了Onclick方法。

情形8:

在情形7的代码中将onTouch方法的返回值该true。
相信大家已经猜出来了,改为true之后touch事件将不被button的onTouchEvent捕获而是直接被消费了,从日志也可以看出:
但是比较奇怪的是,onClick方法也没用被执行,我们猜测onClick方法是在button的onTouchEvent方法中被执行的。事实也确实如此:
在view的onTouchEvent方法中有这样一段逻辑:
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true);
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;

在ACTION_UP分支上执行了click操作,具体由performClick方法执行:

public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
return true;
}
return false;
}

情形9:

以上是在activity中加入显示控件(TextView,Button等),下面在activity中加入容器控件(LinearLayout等),此类控件继承ViewGroup,除了拥有dispatchTouchEvent和ontouchEvent之外,还有onInterceptTouchEvent方法,这个方法用于拦截touch事件,默认返回false,表示不拦截。
下面我们自己实现一个容器控件,并复写onTouchEvent,dispatchTouchEvent和onInterceptTouchEvent:
package com.example.toucheventdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class MyLinearLayout extends LinearLayout
{
private static final String TAG = "MyLinearLayout";
public MyLinearLayout(Context context)
{
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
} @Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onTouchEvent--ACTION_UP");
}
return super.onTouchEvent(event);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
}
return super.dispatchTouchEvent(ev);
} @Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"onInterceptTouchEvent--ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"onInterceptTouchEvent--ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"onInterceptTouchEvent--ACTION_UP");
}
return super.onInterceptTouchEvent(ev);
} }

此时再点击按钮,查看日志:

可以看到,由于加了一层容器控件,所以activity执行完dispatchTouchEvent之后将touch事件分发给容器控件MyLinearLayout,紧接着并不是直接将touch事件传递给button,而是先执行了onInterceptTouchEvent,这个方法返回false,并没有拦截touch事件,所以接下来会将touch事件传递给button。

情形10:
将MyLinearLayout的onInterceptTouchEvent方法返回值置为true,拦截touch事件,使其不再向下传递,点击按钮,查看日志:
可以看到,touch事件再被拦截之后就不再传递给button了,而是被MyLinearLayout的onTouchEvent接收,接着由MainActivity的onTouchEvent接收并消费,ACTION_UP事件将直接由Activity处理。
 
 
总结:
1.事件传递的两种方式:

  • 隧道方式:从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。
  • 冒泡方式:从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。
2.android对Touch Event的分发逻辑是View从上层分发到下层(dispatchTouchEvent函数)类似于隧道方式,然后下层优先开始处理Event(先mOnTouchListener,再onTouchEvent)并向上返回处理情况(boolean值),若返回true,则上层不再处理,类似于冒泡方式。
 
 
 
 

touch事件的分发和消费机制的更多相关文章

  1. Android事件分发机制(一) Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  2. Android 编程下 Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  3. Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  4. Android 编程下Touch 事件的分发和消费机制

    1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...

  5. 事件之Touch 事件的分发和消费机制

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev). ...

  6. Android 编程下Touch 事件的分发和消费机制和OnTouchListener,OnClickListener和OnLongClickListener的关系

    1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev) Touch 事件发生时 Activity 的 dispatchTouchEvent(M ...

  7. 通俗理解Android事件分发与消费机制

    深入:Android Touch事件传递机制全面解析(从WMS到View树) 通俗理解Android事件分发与消费机制 说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与Li ...

  8. View,ViewGroup的Touch事件的分发机制

    原帖地址:http://blog.csdn.net/xiaanming/article/details/21696315 ViewGroup的事件分发机制 我们用手指去触摸Android手机屏幕,就会 ...

  9. Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制

    转自:xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/21696315) 今天这篇文章主要分析的是Android的事件分发机制, ...

随机推荐

  1. 【Bootstrap Method】Evaluating The Accuracy of a Classifier

    自助法介绍: 非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法,也称为自助法.其核心思想和基本步骤如下:(1)采用重抽样技术从原始样本中抽取一定数量(自己给定)的样本,此过程允许重复抽样 ...

  2. maven copy jar 插件

     插件比较特殊 eclipse下的 首先声明插件 <pluginManagement> <plugin> <groupId>org.apache.maven.plu ...

  3. Delphi调用JAVA的WebService上传XML文件(XE10.2+WIN764)

    相关资料:1.http://blog.csdn.net/luojianfeng/article/details/512198902.http://blog.csdn.net/avsuper/artic ...

  4. C++类默认函数

    问题,which is true??? 每个类都有一个无参构造函数 每个类都有一个拷贝构造函数 每个类可以有多个构造函数 每个类可以多个析构函数 默认构造函数   析构函数   拷贝构造函数   赋值 ...

  5. Grunt经常使用插件及演示样例说明

    下述给出了经常使用Grunt插件,并列举了部分插件演示样例: 插件名称 说明 Github地址 grunt-contrib-clean 清空文件和目录 https://github.com/grunt ...

  6. vagrant中css,img不生效的问题

    用vagrant搭建了一个共享开发平台,修改css文件后,页面看不到效果,通过查看源文件地址,发现新修改的Css文件并没有加载进来,加载的还是旧的文件地址.一开始以为是浏览器有缓存,清空了各种浏览器缓 ...

  7. php多进程编程相关资料(以备参考)

    进程与线程的区别 要了解二者的区别与联系,首先得对进程与线程有一个宏观上的了解. 进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位.每一个进程都 ...

  8. Android——浅谈HTTP中Get与Post的区别(转)

    原文地址:http://network.51cto.com/art/201407/446434.htm Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DEL ...

  9. springcloud服务已经关但是Eureka还是显示up

    该状态持续很久,访问该服务也返回错误,但在注册中心界面,该服务却一直存在,且为UP状态,并且在大约十分钟后,出现一行红色大字:EMERGENCY! EUREKA MAY BE INCORRECTLY ...

  10. Navi.Soft31.开发工具(含下载地址)

    1系统简介 1.1功能简述 在软件开发过程中,我们需要经常对字符串.文件.数据库操作.有时需要浏览Json格式串,有时需要浏览Xml格式串,有时需要读取txt或excel文件,有时需要对数据库访问.本 ...