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

来看一些例子。

情形1

只有activity,没有子控件:
代码如下:
  1. package com.example.toucheventdemo;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. public class MainActivity extends Activity
  7. {
  8. private static final String TAG = "MainActivity";
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState)
  11. {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.activity_main);
  14. }
  15. @Override
  16. public boolean dispatchTouchEvent(MotionEvent ev)
  17. {
  18. switch (ev.getAction())
  19. {
  20. case MotionEvent.ACTION_DOWN:
  21. Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
  22. break;
  23. case MotionEvent.ACTION_MOVE:
  24. Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
  25. break;
  26. case MotionEvent.ACTION_UP:
  27. Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
  28. break;
  29. }
  30. return super.dispatchTouchEvent(ev);
  31. }
  32. @Override
  33. public boolean onTouchEvent(MotionEvent event)
  34. {
  35. switch (event.getAction())
  36. {
  37. case MotionEvent.ACTION_DOWN:
  38. Log.i(TAG,"onTouchEvent--ACTION_DOWN");
  39. break;
  40. case MotionEvent.ACTION_MOVE:
  41. Log.i(TAG,"onTouchEvent--ACTION_MOVE");
  42. break;
  43. case MotionEvent.ACTION_UP:
  44. Log.i(TAG,"onTouchEvent--ACTION_UP");
  45. break;
  46. }
  47. return super.onTouchEvent(event);
  48. }
  49. }

日志信息:

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

情形2:

将上面代码dispatchTouchEvent的返回值改为true。
日志:
 
可以看到,只执行了dispatchTouchEvent,而没有执行onTouchEvent。说明在activity中dispatchTouchEvent先于onTouchEvent执行,如果将dispatchTouchEvent返回值置为true,表示事件被消费了,不再传递。
 
 
 
 情形3:
 加入一个子控件
 以自定义Button为例:
  1. package com.example.toucheventdemo;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.widget.Button;
  7. public class MyButton extends Button
  8. {
  9. private static final String TAG = "MyButton";
  10. public MyButton(Context context)
  11. {
  12. super(context);
  13. }
  14. public MyButton(Context context, AttributeSet attrs)
  15. {
  16. super(context, attrs);
  17. }
  18. @Override
  19. public boolean dispatchTouchEvent(MotionEvent ev)
  20. {
  21. switch (ev.getAction())
  22. {
  23. case MotionEvent.ACTION_DOWN:
  24. Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
  25. break;
  26. case MotionEvent.ACTION_MOVE:
  27. Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
  28. break;
  29. case MotionEvent.ACTION_UP:
  30. Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
  31. break;
  32. }
  33. return super.dispatchTouchEvent(ev);
  34. }
  35. @Override
  36. public boolean onTouchEvent(MotionEvent event)
  37. {
  38. switch (event.getAction())
  39. {
  40. case MotionEvent.ACTION_DOWN:
  41. Log.i(TAG,"onTouchEvent--ACTION_DOWN");
  42. break;
  43. case MotionEvent.ACTION_MOVE:
  44. Log.i(TAG,"onTouchEvent--ACTION_MOVE");
  45. break;
  46. case MotionEvent.ACTION_UP:
  47. Log.i(TAG,"onTouchEvent--ACTION_UP");
  48. break;
  49. }
  50. return super.onTouchEvent(event);
  51. }
  52. }

mainActivity代码如下:

  1. package com.example.toucheventdemo;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. public class MainActivity extends Activity
  9. {
  10. private static final String TAG = "MainActivity";
  11. private MyButton but = null;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17.  
  18. but = (MyButton) findViewById(R.id.but);
  19. }
  20. @Override
  21. public boolean dispatchTouchEvent(MotionEvent ev)
  22. {
  23. switch (ev.getAction())
  24. {
  25. case MotionEvent.ACTION_DOWN:
  26. Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
  27. break;
  28. case MotionEvent.ACTION_MOVE:
  29. Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
  30. break;
  31. case MotionEvent.ACTION_UP:
  32. Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
  33. break;
  34. }
  35. return super.dispatchTouchEvent(ev);
  36. }
  37. @Override
  38. public boolean onTouchEvent(MotionEvent event)
  39. {
  40. switch (event.getAction())
  41. {
  42. case MotionEvent.ACTION_DOWN:
  43. Log.i(TAG,"onTouchEvent--ACTION_DOWN");
  44. break;
  45. case MotionEvent.ACTION_MOVE:
  46. Log.i(TAG,"onTouchEvent--ACTION_MOVE");
  47. break;
  48. case MotionEvent.ACTION_UP:
  49. Log.i(TAG,"onTouchEvent--ACTION_UP");
  50. break;
  51. }
  52. return super.onTouchEvent(event);
  53. }
  54.  
  55. }

此时点击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
  1. package com.example.toucheventdemo;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. import android.view.View.OnTouchListener;
  9. public class MainActivity extends Activity implements OnClickListener,OnTouchListener
  10. {
  11. private static final String TAG = "MainActivity";
  12. private MyButton but = null;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18.  
  19. but = (MyButton) findViewById(R.id.but);
  20. but.setOnClickListener(this);
  21. but.setOnTouchListener(this);
  22. }
  23. @Override
  24. public boolean dispatchTouchEvent(MotionEvent ev)
  25. {
  26. switch (ev.getAction())
  27. {
  28. case MotionEvent.ACTION_DOWN:
  29. Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
  30. break;
  31. case MotionEvent.ACTION_MOVE:
  32. Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
  33. break;
  34. case MotionEvent.ACTION_UP:
  35. Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
  36. break;
  37. }
  38. return super.dispatchTouchEvent(ev);
  39. }
  40. @Override
  41. public boolean onTouchEvent(MotionEvent event)
  42. {
  43. switch (event.getAction())
  44. {
  45. case MotionEvent.ACTION_DOWN:
  46. Log.i(TAG,"onTouchEvent--ACTION_DOWN");
  47. break;
  48. case MotionEvent.ACTION_MOVE:
  49. Log.i(TAG,"onTouchEvent--ACTION_MOVE");
  50. break;
  51. case MotionEvent.ACTION_UP:
  52. Log.i(TAG,"onTouchEvent--ACTION_UP");
  53. }
  54. return super.onTouchEvent(event);
  55. }
  56. @Override
  57. public void onClick(View v)
  58. {
  59. Log.i("MyButton","ONCLICK");
  60. }
  61. @Override
  62. public boolean onTouch(View v, MotionEvent event)
  63. {
  64. switch (event.getAction())
  65. {
  66. case MotionEvent.ACTION_DOWN:
  67. Log.i("MyButton","onTouch--ACTION_DOWN");
  68. break;
  69. case MotionEvent.ACTION_MOVE:
  70. Log.i("MyButton","onTouch--ACTION_MOVE");
  71. break;
  72. case MotionEvent.ACTION_UP:
  73. Log.i("MyButton","onTouch--ACTION_UP");
  74. break;
  75. }
  76. return false;
  77. }
  78. }

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

首先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方法中有这样一段逻辑:
  1. case MotionEvent.ACTION_UP:
  2. boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
  3. if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
  4. // take focus if we don't have it already and we should in
  5. // touch mode.
  6. boolean focusTaken = false;
  7. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  8. focusTaken = requestFocus();
  9. }
  10. if (prepressed) {
  11. // The button is being released before we actually
  12. // showed it as pressed. Make it show the pressed
  13. // state now (before scheduling the click) to ensure
  14. // the user sees it.
  15. setPressed(true);
  16. }
  17. if (!mHasPerformedLongPress) {
  18. // This is a tap, so remove the longpress check
  19. removeLongPressCallback();
  20. // Only perform take click actions if we were in the pressed state
  21. if (!focusTaken) {
  22. // Use a Runnable and post this rather than calling
  23. // performClick directly. This lets other visual state
  24. // of the view update before click actions start.
  25. if (mPerformClick == null) {
  26. mPerformClick = new PerformClick();
  27. }
  28. if (!post(mPerformClick)) {
  29. performClick();
  30. }
  31. }
  32. }
  33. if (mUnsetPressedState == null) {
  34. mUnsetPressedState = new UnsetPressedState();
  35. }
  36. if (prepressed) {
  37. postDelayed(mUnsetPressedState,
  38. ViewConfiguration.getPressedStateDuration());
  39. } else if (!post(mUnsetPressedState)) {
  40. // If the post failed, unpress right now
  41. mUnsetPressedState.run();
  42. }
  43. removeTapCallback();
  44. }
  45. break;

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

  1. public boolean performClick() {
  2. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
  3. ListenerInfo li = mListenerInfo;
  4. if (li != null && li.mOnClickListener != null) {
  5. playSoundEffect(SoundEffectConstants.CLICK);
  6. li.mOnClickListener.onClick(this);
  7. return true;
  8. }
  9. return false;
  10. }

情形9:

以上是在activity中加入显示控件(TextView,Button等),下面在activity中加入容器控件(LinearLayout等),此类控件继承ViewGroup,除了拥有dispatchTouchEvent和ontouchEvent之外,还有onInterceptTouchEvent方法,这个方法用于拦截touch事件,默认返回false,表示不拦截。
下面我们自己实现一个容器控件,并复写onTouchEvent,dispatchTouchEvent和onInterceptTouchEvent:
  1. package com.example.toucheventdemo;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.widget.LinearLayout;
  7. public class MyLinearLayout extends LinearLayout
  8. {
  9. private static final String TAG = "MyLinearLayout";
  10. public MyLinearLayout(Context context)
  11. {
  12. super(context);
  13. }
  14. public MyLinearLayout(Context context, AttributeSet attrs)
  15. {
  16. super(context, attrs);
  17. }
  18.  
  19. @Override
  20. public boolean onTouchEvent(MotionEvent event)
  21. {
  22. switch (event.getAction())
  23. {
  24. case MotionEvent.ACTION_DOWN:
  25. Log.i(TAG,"onTouchEvent--ACTION_DOWN");
  26. break;
  27. case MotionEvent.ACTION_MOVE:
  28. Log.i(TAG,"onTouchEvent--ACTION_MOVE");
  29. break;
  30. case MotionEvent.ACTION_UP:
  31. Log.i(TAG,"onTouchEvent--ACTION_UP");
  32. }
  33. return super.onTouchEvent(event);
  34. }
  35.  
  36. @Override
  37. public boolean dispatchTouchEvent(MotionEvent ev)
  38. {
  39. switch (ev.getAction())
  40. {
  41. case MotionEvent.ACTION_DOWN:
  42. Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");
  43. break;
  44. case MotionEvent.ACTION_MOVE:
  45. Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");
  46. break;
  47. case MotionEvent.ACTION_UP:
  48. Log.i(TAG,"dispatchTouchEvent--ACTION_UP");
  49. }
  50. return super.dispatchTouchEvent(ev);
  51. }
  52.  
  53. @Override
  54. public boolean onInterceptTouchEvent(MotionEvent ev)
  55. {
  56. switch (ev.getAction())
  57. {
  58. case MotionEvent.ACTION_DOWN:
  59. Log.i(TAG,"onInterceptTouchEvent--ACTION_DOWN");
  60. break;
  61. case MotionEvent.ACTION_MOVE:
  62. Log.i(TAG,"onInterceptTouchEvent--ACTION_MOVE");
  63. break;
  64. case MotionEvent.ACTION_UP:
  65. Log.i(TAG,"onInterceptTouchEvent--ACTION_UP");
  66. }
  67. return super.onInterceptTouchEvent(ev);
  68. }
  69.  
  70. }

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

可以看到,由于加了一层容器控件,所以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. u盘引导制作工具

    https://rufus.ie/en_IE.html

  2. mybatis批量更新的两种实现方式

    mapper.xml文件,后台传入一个对象集合,另外如果是mysql数据库,一点在配置文件上加上&allowMultiQueries=true,这样才可以执行多条sql,以下为mysql: & ...

  3. Spring boot 报错 Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

    在实际开发中修改别人的代码,发现了这个报错,后来发现是因为pom.xml里面 只要将注释掉的部分注释掉就好了.

  4. [转]MySQL update join语句

    原文地址:https://www.jianshu.com/p/f99665266bb1 在本教程中,您将学习如何使用MySQL UPDATE JOIN语句来执行跨表更新.我们将逐步介绍如何使用INNE ...

  5. Spring Bean装配学习

    解释:所谓装配就是把一个类需要的组件给它设置进去,英文就是wire,wiring:注解Autowire也叫自动装配. 目前Spring提供了三种配置方案: 在XML中进行显式的配置 在Java中进行显 ...

  6. python re 库的使用

    python re 库是关于正则表达式的一个库.这里面包含了多种字符串匹配的方法 使用例程 # -*- coding: utf-8 -*- import re # search 的作用是 查找后面字符 ...

  7. 使用taskset命令来限制进程的CPU

    常常感觉系统资源不够用,一台机子上跑了不下3个比较重要的服务,但是每天我们还要在上面进行个备份压缩等处理,网络长时间传输,这在就很影响本就不够用的系统资源: 这个时候我们就可以把一些不太重要的比如co ...

  8. 【转载】解决Windows和Ubuntu时间不一致的问题

    大家在切换操作系统的时候会发现一个问题, Windows 和Ubuntu的时间会出现不一致的情况.在 Windows 中把时间设置正确了过后,回到在 Ubuntu 后系统的时间又不一样了,在 Ubun ...

  9. jQuery(六):value值操作

    val()可以获取或设置元素的value属性值.语法如下: 示例: <!DOCTYPE html> <html lang="en"> <head> ...

  10. 想做web开发 就学JavaScript

    有一天我被问到,为了快速地在 web 开发工作上增加优势,应该学习什么语言.我的思绪回到了大学,那时候我用 Pascal.Fortran.C和汇编语言,不过那个时候有不同的目标. 鉴于当前的状况和趋势 ...