Android开发之触摸事件处理机制详解

简介:

android触碰消息传递机制



用户的每次触碰(onClick,onLongClick,onScroll,etc.)都是由一个ACTION_DOWN+n个ACTION_MOVE+1个ACTION_UP组成的,用户触碰必先有个ACTION_DOWN响应,用户触碰结束必然会有个ACTION_UP。(当然如果在途中被拦截,就可能不会有了!)那么View是如何分发消息和拦截消息呢?


1.View及其子类都会有的两个方法:


public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
2.特殊的View子类ViewGroup则还有一个方法:


public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3.分发


dispatchTouchEvent 收到触碰,则向最外层的View传递消息,再向子层的View分发
4.拦截:


onInterceptTouchEvent 拦截返回true表示要拦截消息,不要再向子View传递(这里的子View不是继承关系,而是包容关系)。返回false则表示不拦截消息,可以继续向下一层级的View传递消息,子View将可以dispatchTouchEvent 收到触碰消息再分发消息


5.消息处理:


onTouchEvent 处理事件,拦截了消息,或者是最后一个收到消息的View调用此方法来处理事件,若返回true,则表示正确接收并处理。若返回false则表示没有被处理,将向父View传递(这里的父View不是继承关系,而是包容关系)


Android事件模型之interceptTouchEvnet ,onTouchEvent关系正解

参考文档:

http://blog.csdn.net/liutao5757124/article/details/6097125


首先,看Android的官方文档正解


onInterceptTouchEvent()与onTouchEvent()的机制:

  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()处理

这是官方文档的说法,要是自己没亲自去写个程序观察哈,基本上没法理解,所以上程序先,然后分析:



布局文件main.xml

Java代码   收藏代码
  1. <span style="font-size: small;"><?xml version="1.0" encoding="utf-8"?>  
  2. <com.hao.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical" android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.     <com.hao.LayoutView2  
  6.         android:orientation="vertical" android:layout_width="fill_parent"  
  7.         android:layout_height="fill_parent" android:gravity="center">  
  8.         <com.hao.MyTextView  
  9.             android:layout_width="wrap_content" android:layout_height="wrap_content"  
  10.             android:id="@+id/tv" android:text="AB" android:textSize="40sp"  
  11.             android:textStyle="bold" android:background="#FFFFFF"  
  12.             android:textColor="#0000FF" />  
  13.     </com.hao.LayoutView2>  
  14. </com.hao.LayoutView1>   
  15. </span>  

 第一层自定义布局LayoutView1.java

Java代码   收藏代码
  1. <span style="font-size: small;">package com.hao;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.LinearLayout;  
  8.   
  9. public class LayoutView1 extends LinearLayout {  
  10.      private final String TAG = "LayoutView1";   
  11.      public LayoutView1(Context context, AttributeSet attrs) {   
  12.          super(context, attrs);   
  13.          Log.e(TAG,TAG);   
  14.      }  
  15.        
  16.      @Override   
  17.      public boolean onInterceptTouchEvent(MotionEvent ev) {   
  18.          int action = ev.getAction();   
  19.          switch(action){   
  20.          case MotionEvent.ACTION_DOWN:   
  21.               Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");   
  22. //              return true; 在这就拦截了,后面的就不会得到事件  
  23.               break;  
  24.         case MotionEvent.ACTION_MOVE:   
  25.               Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE");   
  26.               break;   
  27.          case MotionEvent.ACTION_UP:   
  28.               Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP");   
  29.               break;   
  30.          case MotionEvent.ACTION_CANCEL:   
  31.               Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");   
  32.               break;   
  33.          }   
  34.          return false;   
  35.      }   
  36.   
  37.      @Override   
  38.      public boolean onTouchEvent(MotionEvent ev) {   
  39.          int action = ev.getAction();   
  40.          switch(action){   
  41.          case MotionEvent.ACTION_DOWN:   
  42.               Log.e(TAG,"onTouchEvent action:ACTION_DOWN");   
  43.               break;   
  44.          case MotionEvent.ACTION_MOVE:   
  45.               Log.e(TAG,"onTouchEvent action:ACTION_MOVE");   
  46.               break;   
  47.          case MotionEvent.ACTION_UP:   
  48.               Log.e(TAG,"onTouchEvent action:ACTION_UP");   
  49.               break;   
  50.          case MotionEvent.ACTION_CANCEL:   
  51.               Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");   
  52.               break;   
  53.          }   
  54.          return true;   
  55. //         return false;  
  56.      }   
  57.        
  58.      @Override   
  59.      protected void onLayout(boolean changed, int l, int t, int r, int b) {   
  60.          // TODO Auto-generated method stub   
  61.          super.onLayout(changed, l, t, r, b);   
  62.      }   
  63.   
  64.      @Override   
  65.      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {   
  66.          // TODO Auto-generated method stub   
  67.          super.onMeasure(widthMeasureSpec, heightMeasureSpec);   
  68.      }   
  69. }</span>  

 第二层布局LayoutView2.java

Java代码   收藏代码
  1. <span style="font-size: small;">package com.hao;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.LinearLayout;  
  8.   
  9. public class LayoutView2 extends LinearLayout {  
  10.       
  11.     private final String TAG = "LayoutView2";   
  12.     public LayoutView2(Context context, AttributeSet attrs) {   
  13.        super(context, attrs);   
  14.        Log.e(TAG,TAG);   
  15.     }   
  16.       
  17.     @Override   
  18.     public boolean onInterceptTouchEvent(MotionEvent ev) {   
  19.        int action = ev.getAction();   
  20.        switch(action){   
  21.        case MotionEvent.ACTION_DOWN:   
  22.            Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");  
  23. //           return true;  
  24.            break;   
  25.        case MotionEvent.ACTION_MOVE:   
  26.            Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE");   
  27.            break;   
  28.        case MotionEvent.ACTION_UP:   
  29.            Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP");   
  30.            break;   
  31.        case MotionEvent.ACTION_CANCEL:   
  32.            Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");   
  33.            break;   
  34.        }   
  35.        return false;   
  36.     }   
  37.   
  38.     @Override   
  39.     public boolean onTouchEvent(MotionEvent ev) {   
  40.        int action = ev.getAction();   
  41.        switch(action){   
  42.        case MotionEvent.ACTION_DOWN:   
  43.            Log.e(TAG,"onTouchEvent action:ACTION_DOWN");   
  44.            break;   
  45.        case MotionEvent.ACTION_MOVE:   
  46.            Log.e(TAG,"onTouchEvent action:ACTION_MOVE");   
  47.            break;   
  48.        case MotionEvent.ACTION_UP:   
  49.            Log.e(TAG,"onTouchEvent action:ACTION_UP");   
  50.            break;   
  51.        case MotionEvent.ACTION_CANCEL:   
  52.            Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");   
  53.            break;   
  54.        }   
  55. //       return true;   
  56.        return false;  
  57.     }   
  58. }  
  59. </span>  

 自定义MyTextView.java

Java代码   收藏代码
  1. <span style="font-size: small;">package com.hao;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.widget.TextView;  
  9.   
  10. public class MyTextView extends TextView {  
  11.     private final String TAG = "MyTextView";   
  12.     public MyTextView(Context context, AttributeSet attrs) {   
  13.        super(context, attrs);   
  14.        Log.e(TAG,TAG);   
  15.     }   
  16.   
  17.     @Override   
  18.     public boolean onTouchEvent(MotionEvent ev) {   
  19.        int action = ev.getAction();   
  20.        switch(action){   
  21.        case MotionEvent.ACTION_DOWN:   
  22.            Log.e(TAG,"onTouchEvent action:ACTION_DOWN");   
  23.            break;   
  24.        case MotionEvent.ACTION_MOVE:   
  25.            Log.e(TAG,"onTouchEvent action:ACTION_MOVE");   
  26.            break;   
  27.        case MotionEvent.ACTION_UP:   
  28.            Log.e(TAG,"onTouchEvent action:ACTION_UP");   
  29.            break;   
  30.        case MotionEvent.ACTION_CANCEL:   
  31.            Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");   
  32.            break;   
  33.        }   
  34.        return false;   
  35. //       return true;  
  36.     }   
  37.   
  38.     public void onClick(View v) {   
  39.        Log.e(TAG, "onClick");   
  40.     }   
  41.   
  42.     public boolean onLongClick(View v) {   
  43.        Log.e(TAG, "onLongClick");   
  44.        return false;   
  45.     }   
  46. }  
  47. </span>  

 其实代码很简单,就是自定义了View,在View里面都重写了interceptTouchEvnet (),和onTouchEvent(),然后测试其返回值,对监听的影响,关键是自己动手,逐个测试,并预测结果,等你能预测结果的时候,也就懂了,需要修改的地方就是interceptTouchEvnet 和onTouchEvent的返回值,他们决定了事件监听的流程,下面我画了一张图,如有不足之处欢迎指正,谢谢!


下面是我的正解:

基本的规则是: 
      *1.down事件首先会传递到onInterceptTouchEvent()方法 
      * 
      * 2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false(不拦截),
      * 那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。 
      *
      * 3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true(拦截,那么后面的move,up事件不需要在看因为已经拦截了, 我们直接拿去处理onTouchEvent()就可以了),那么后续的move, up等事件将不再传递给onInterceptTouchEvent(), 而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。 下面例子演示:
      * 1:LayoutView1(31375): onInterceptTouchEvent action:ACTION_DOWN
      * 2:LayoutView2(31375): onInterceptTouchEvent action:ACTION_DOWN
      * 3:LayoutView2(31375): onTouchEvent action:ACTION_DOWN
      * 4:LayoutView1(31375): onInterceptTouchEvent action:ACTION_MOVE
      * 5:LayoutView2(31375): onTouchEvent action:ACTION_MOVE
      * 6:LayoutView1(31375): onInterceptTouchEvent action:ACTION_MOVE
      * 7:LayoutView2(31375): onTouchEvent action:ACTION_MOVE
      * 8:LayoutView1(31375): onInterceptTouchEvent action:ACTION_UP
      * 9:LayoutView2(31375): onTouchEvent action:ACTION_UP
      * 该设置为:
      * onInterceptTouchEvent:LayoutView1为false,LayoutView2为true
      * onTouchEvent:LayoutView2为true
      * 故而事件在LayoutView2(onInterceptTouchEvent:返回true)时被拦截并处理,根据上面说法就是LayoutView2后续的MOVE,UP操作都不在经过onInterceptTouchEvent,直接
      * 交给onTouchEvent处理,结果也的确如此。(见:LayoutView2的3,5,7,9,第一次是onInterceptTouchEvent处理如1,以后交给onTouchEvent)
      * 而LayoutView1都还是要经过onInterceptTouchEvent(见LayoutView1的4,6,8)
      * 
      * 4.如果最终需要处理事件的view的onTouchEvent()返回了false(没能处理这个事件,不能丢在传回来让父继续),
      * 那么该事件将被传递至其上一层次的view的onTouchEvent()处理。 
      * **************************************************************************
      * 感觉像是一个圈,然后一直在找一个能处理这个消息的人,如果找到了就结束,没找到就循环,直到回到发出消息的那个人
      * 注(对下面):没有标注的DOWN表示拦截事件onInterceptTouchEvent,标注了onTouchEvent就是处理事件
      * a.如果都没处理(onInterceptTouchEvent返回false):
A(DOWN)-->B(DOWN)-->C(onTouchEvent DOWN)-->B(onTouchEvent DOWN)-->A(onTouchEvent DOWN),没有执行UP事件,注意有MOVE的话,在DOWN和UP之间,下面的都一样。
      *b. B处理(B的onInterceptTouchEvent返回true):
A(DOWN)-->B(DOWN)-->B(onTouchEvent)-->A(onTouchEvent UP)-->B(onTouchEvent UP)-->(over)
      *
形象说明:如果父亲不拦截消息就传给儿子,如果儿子要这个消息就处理(DOWN),结束,然后有父亲1--父亲2--儿子以此释放消息(UP)。 然是如果儿子对这个消息置之不理,那这个消息又传回父亲,由父亲来处理即。

下面给出了5中情况(不拦截表示onInterceptTouchEvent返回false):
      * 11** 父亲1(LayoutView1不拦截false)---父亲2(LayoutView2不拦截false)--儿子(MyTextView,onTouchEvent return true)--结束
      * 22** 父亲1(LayoutView1不拦截false)---父亲2(LayoutView2不拦截false)--儿子(MyTextView,onTouchEvent return false)--回传给父亲2(onTouchEvent return true)--结束
      * 33** 父亲1(LayoutView1不拦截false)---父亲2(LayoutView2不拦截false)--儿子(MyTextView,onTouchEvent return false)--回传给父亲2(onTouchEvent return false)--父亲1(onTouchEvent return true)--结束(如果都没处理不在执行UP ACTION)
      * 44** 父亲1(LayoutView1拦截true)--父亲1(onTouchEvent return true)--结束          (DOWN--DOWN(onTouchEvent)--UP(onTouchEvent))
      * 55** 父亲1(LayoutView1拦截false)--父亲2(LayoutView2拦截true)--父亲2(onTouchEvent return false)--父亲1(onTouchEvent return true)--结束      (DOWN1--DOWN2--DOWN(2 onTouchEvent)--DOWN(1 onTouchEvent)--UP(1 onTouchEvent))(1:父亲2,2:父亲2)
      * 
      * ***************************************************************************
      * 5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。 
      */

下面给出一张处理的流程图:


 下附源代码:

相关文章
|
18天前
|
算法 Linux 调度
深入探索安卓系统的多任务处理机制
【10月更文挑战第21天】 本文旨在为读者提供一个关于Android系统多任务处理机制的全面解析。我们将从Android操作系统的核心架构出发,探讨其如何管理多个应用程序的同时运行,包括进程调度、内存管理和电量优化等方面。通过深入分析,本文揭示了Android在处理多任务时所面临的挑战以及它如何通过创新的解决方案来提高用户体验和设备性能。
32 1
|
18天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
24天前
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
24天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
6天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
33 19
|
10天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
26天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
6天前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
13 0
|
Java API Android开发
Android触摸事件传递分析与实践
设计UI时,亲爱的交互设计师们总会有一些天马行空的想法,大多数情况下原生的控件已不能支持这些“看似简单”的交互逻辑,需要继承`ListView`、`ViewPager`、`ScrollView`甚至直接继承View来自定义一些特性来支撑。在处理触摸事件时,无可避免的需要重写`onInterceptTouchEvent`与`onTouchEvent`这两个方法。本文将从源码的角度,从这两个棘手的函
1895 0
|
26天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。