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. 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 LayoutView1 extends LinearLayout {
  8. private final String TAG = "LayoutView1";
  9. public LayoutView1(Context context, AttributeSet attrs) {
  10. super(context, attrs);
  11. Log.e(TAG,TAG);
  12. }
  13. @Override
  14. public boolean onInterceptTouchEvent(MotionEvent ev) {
  15. int action = ev.getAction();
  16. switch(action){
  17. case MotionEvent.ACTION_DOWN:
  18. Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
  19. //              return true; 在这就拦截了,后面的就不会得到事件
  20. break;
  21. case MotionEvent.ACTION_MOVE:
  22. Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
  23. break;
  24. case MotionEvent.ACTION_UP:
  25. Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP");
  26. break;
  27. case MotionEvent.ACTION_CANCEL:
  28. Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
  29. break;
  30. }
  31. return false;
  32. }
  33. @Override
  34. public boolean onTouchEvent(MotionEvent ev) {
  35. int action = ev.getAction();
  36. switch(action){
  37. case MotionEvent.ACTION_DOWN:
  38. Log.e(TAG,"onTouchEvent action:ACTION_DOWN");
  39. break;
  40. case MotionEvent.ACTION_MOVE:
  41. Log.e(TAG,"onTouchEvent action:ACTION_MOVE");
  42. break;
  43. case MotionEvent.ACTION_UP:
  44. Log.e(TAG,"onTouchEvent action:ACTION_UP");
  45. break;
  46. case MotionEvent.ACTION_CANCEL:
  47. Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");
  48. break;
  49. }
  50. return true;
  51. //         return false;
  52. }
  53. @Override
  54. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  55. // TODO Auto-generated method stub
  56. super.onLayout(changed, l, t, r, b);
  57. }
  58. @Override
  59. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  60. // TODO Auto-generated method stub
  61. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  62. }
  63. }</span>

第二层布局LayoutView2.java

Java代码  

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

自定义MyTextView.java

Java代码  

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

*/

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

下附源代码:

时间: 2024-08-10 23:28:06

Android开发之触摸事件处理机制详解的相关文章

Android ViewGroup触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android View触摸屏事件派发机制详解与源码分析>,阅读本篇之前建议先阅读. 1 背景 还记得前一篇<Android View触摸屏事件派发机制详解与源码分析>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事

Android开发技巧之viewstub用法详解及实现延迟加载

这一篇是接着上面的include标签的例子来讲的,地址http://blog.csdn.net/jason0539/article/details/26131831 上一篇的布局中间就用了viewstub这个控件,现在来说一下其作用和用法 " ViewStub 是一个不可见的,大小为0的View,最佳用途就是实现View的延迟加载,避免资源浪费,在需要的时候才加载View " 需要注意的是,加载view之后,viewstub本身就会被新加载进来的view替换掉 上代码了,看完就理解了

Android的事件处理机制详解(二)-----基于监听的事件处理机制

基于监听的事件处理机制 前言: 我们开发的app更多的时候是需要与用户的交互----即对用户的操作进行响应 这就涉及到了android的事件处理机制; android给我们提供了两套功能强大的处理机制: ①基于监听的事件处理机制 ②基于回调的事件处理机制 在这一节中,我们会先介绍一下基于监听的事件处理机制 好了,废话不多说! 我们要先了解一下监听处理机制的模型 监听的处理模型: 处理模型图: 文字表述: 事件监听机制中由事件源,事件,事件监听器三类对象组成 处理流程: step 1:为某个事件源

Android View触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 1.背景 最近在简书和微博还有Q群看见很多人说Android自定义控件(View/ViewGroup)如何学习?为啥那么难?其实答案很简单:"基础不牢,地动山摇." 不扯蛋了,进入正题.就算你不自定义控件,你也必须要了解Android控件的触摸屏事件传递机制(之所以说触摸屏是因为该

Android开发教程复选框详解

前面麦子学院的android开发老师给大家介绍过关于Android开发教程单选框详解,今天麦子学院的android开发老师给大家讲android复选框的一些基本内容. ●设置复选框的Check状态的时候,调用setChecked()方法 ●追加Android复选框被选择时处理的时候, 1.调用setOnCheckedChangeListener()方法,并把CompoundButton.OnCheckedChangeListener实例作为参数传入 2.在CompoundButton.OnChe

Android开发数据存储之ContentProvider详解

转载:十二.ContentProvider和Uri详解 一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查.关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_W

Android开发5大布局方式详解

Android中常用的5大布局方式有以下几种: 线性布局(LinearLayout):按照垂直或者水平方向布局的组件. 帧布局(FrameLayout):组件从屏幕左上方布局组件. 表格布局(TableLayout):按照行列方式布局组件. 相对布局(RelativeLayout):相对其它组件的布局方式. 绝对布局(AbsoluteLayout):按照绝对坐标来布局组件. 1. 线性布局 线性布局是Android开发中最常见的一种布局方式,它是按照垂直或者水平方向来布局,通过“android:

Android笔记02:Intent机制详解

一.什么是Intent? Intent在Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用.Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互.因此,可以将Intent理解为不同组件之间通信的"媒介",专门提供组件互相调用的相关信息. 二.In

Android开发——Android 6.0权限管理机制详解

0.前言 最近在研究所实习,我负责维护Android手机取证项目的Android客户端,有客户反映我们的APP在Android6.0无响应,经过调试发现SD卡读写权限权限被拒绝.但明明是在AndroidManifest.xml文件中声明过的.查了很多资料才知道Android6.0的很多权限申请机制发生了改变,可以说是Android6.0在安全机制上更进了一步吧,因此写下这篇文章以记录. 注:在运行程序时,对于某些权限向用户询问申请(后面会详细地讲)时因为我们知道客户在我们APP中不会点"拒绝&q