出处:https://blog.csdn.net/caifengyao/article/details/65437695

在Android中,View的结构是树状的,所以,当触发触摸事件的时候,其事件传递也是从上之下一层层的传递。下面我们结合例子来一点点进行分析。

首先,我们需要了解事件处理中的几个方法:

1、在ViewGroup中,事件分为dispatchTouchEvent(事件的分发),onInterceptTouchEvent(事件的拦截),onTouchEvent(事件的处理)。

2、在View中,事件分为dispatchTouchEvent(事件的分发),onTouchEvent(事件的处理)。

下面是demo的界面结构,它是由两个自定义的ViewGroup和一个自定义的View组成,并分别重写了它们的以上几个方法。

其中MyViewGroupA代码如下:

  1. public class MyViewGroupA extends LinearLayout {
  2. public MyViewGroupA(Context context) {
  3. super(context);
  4. }
  5.  
  6. public MyViewGroupA(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9.  
  10. @Override
  11. public boolean dispatchTouchEvent(MotionEvent ev) {
  12. switch (ev.getAction()){
  13. case MotionEvent.ACTION_DOWN:
  14. Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_DOWN");
  15. break;
  16. case MotionEvent.ACTION_MOVE:
  17. Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_MOVE");
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_UP");
  21. break;
  22. }
  23. return super.dispatchTouchEvent(ev);
  24. }
  25.  
  26. @Override
  27. public boolean onInterceptTouchEvent(MotionEvent ev) {
  28. switch (ev.getAction()){
  29. case MotionEvent.ACTION_DOWN:
  30. Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_DOWN");
  31. break;
  32. case MotionEvent.ACTION_MOVE:
  33. Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_MOVE");
  34. break;
  35. case MotionEvent.ACTION_UP:
  36. Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_UP");
  37. break;
  38. }
  39. return super.onInterceptTouchEvent(ev);
  40. }
  41.  
  42. @Override
  43. public boolean onTouchEvent(MotionEvent event) {
  44. switch (event.getAction()){
  45. case MotionEvent.ACTION_DOWN:
  46. Log.i("MyViewGroupA","onTouchEvent_ACTION_DOWN");
  47. break;
  48. case MotionEvent.ACTION_MOVE:
  49. Log.i("MyViewGroupA","onTouchEvent_ACTION_MOVE");
  50. break;
  51. case MotionEvent.ACTION_UP:
  52. Log.i("MyViewGroupA","onTouchEvent_ACTION_UP");
  53. break;
  54. }
  55. return super.onTouchEvent(event);
  56. }
  57. }

MyViewGroupB代码如下:

  1. public class MyViewGroupB extends LinearLayout {
  2. public MyViewGroupB(Context context) {
  3. super(context);
  4. }
  5.  
  6. public MyViewGroupB(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9.  
  10. @Override
  11. public boolean dispatchTouchEvent(MotionEvent ev) {
  12. switch (ev.getAction()){
  13. case MotionEvent.ACTION_DOWN:
  14. Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_DOWN");
  15. break;
  16. case MotionEvent.ACTION_MOVE:
  17. Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_MOVE");
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_UP");
  21. break;
  22. }
  23. return super.dispatchTouchEvent(ev);
  24. }
  25.  
  26. @Override
  27. public boolean onInterceptTouchEvent(MotionEvent ev) {
  28. switch (ev.getAction()){
  29. case MotionEvent.ACTION_DOWN:
  30. Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_DOWN");
  31. break;
  32. case MotionEvent.ACTION_MOVE:
  33. Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_MOVE");
  34. break;
  35. case MotionEvent.ACTION_UP:
  36. Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_UP");
  37. break;
  38. }
  39. return super.onInterceptTouchEvent(ev);
  40. }
  41.  
  42. @Override
  43. public boolean onTouchEvent(MotionEvent event) {
  44. switch (event.getAction()){
  45. case MotionEvent.ACTION_DOWN:
  46. Log.i("MyViewGroupB","onTouchEvent_ACTION_DOWN");
  47. break;
  48. case MotionEvent.ACTION_MOVE:
  49. Log.i("MyViewGroupB","onTouchEvent_ACTION_MOVE");
  50. break;
  51. case MotionEvent.ACTION_UP:
  52. Log.i("MyViewGroupB","onTouchEvent_ACTION_UP");
  53. break;
  54. }
  55. return super.onTouchEvent(event);
  56. }
  57. }

MyView代码如下:

  1. public class MyView extends View {
  2. public MyView(Context context) {
  3. super(context);
  4. }
  5.  
  6. public MyView(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. }
  9.  
  10. @Override
  11. public boolean dispatchTouchEvent(MotionEvent event) {
  12. switch (event.getAction()){
  13. case MotionEvent.ACTION_DOWN:
  14. Log.i("MyView","dispatchTouchEvent_ACTION_DOWN");
  15. break;
  16. case MotionEvent.ACTION_MOVE:
  17. Log.i("MyView","dispatchTouchEvent_ACTION_MOVE");
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.i("MyView","dispatchTouchEvent_ACTION_UP");
  21. break;
  22. }
  23. return super.dispatchTouchEvent(event);
  24. }
  25.  
  26. @Override
  27. public boolean onTouchEvent(MotionEvent event) {
  28. switch (event.getAction()){
  29. case MotionEvent.ACTION_DOWN:
  30. Log.i("MyView","onTouchEvent_ACTION_DOWN");
  31. break;
  32. case MotionEvent.ACTION_MOVE:
  33. Log.i("MyView","onTouchEvent_ACTION_MOVE");
  34. break;
  35. case MotionEvent.ACTION_UP:
  36. Log.i("MyView","onTouchEvent_ACTION_UP");
  37. break;
  38. }
  39. return super.onTouchEvent(event);
  40. }
  41. }

我们说过,事件传递是由上到下的,所以最外层的View首先对事件进行操作。而我们最外层是Activity,所以事件也是从这里开始。

Activity代码如下:

  1. public class MainActivity extends AppCompatActivity {
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. }
  8. @Override
  9. public boolean dispatchTouchEvent(MotionEvent event) {
  10. switch (event.getAction()){
  11. case MotionEvent.ACTION_DOWN:
  12. Log.i("Activity","dispatchTouchEvent_ACTION_DOWN");
  13. break;
  14. case MotionEvent.ACTION_MOVE:
  15. Log.i("Activity","dispatchTouchEvent_ACTION_MOVE");
  16. break;
  17. case MotionEvent.ACTION_UP:
  18. Log.i("Activity","dispatchTouchEvent_ACTION_UP");
  19. break;
  20. }
  21. return super.dispatchTouchEvent(event);
  22. }
  23.  
  24. @Override
  25. public boolean onTouchEvent(MotionEvent event) {
  26. switch (event.getAction()){
  27. case MotionEvent.ACTION_DOWN:
  28. Log.i("Activity","onTouchEvent_ACTION_DOWN");
  29. break;
  30. case MotionEvent.ACTION_MOVE:
  31. Log.i("Activity","onTouchEvent_ACTION_MOVE");
  32. break;
  33. case MotionEvent.ACTION_UP:
  34. Log.i("Activity","onTouchEvent_ACTION_UP");
  35. break;
  36. }
  37. return super.onTouchEvent(event);
  38. }
  39. }

现在我们通过触摸MyView开始进行分析。虽然dispatchTouchEvent是事件开始的第一步,但是在开发中,我们通常很少改写它,所以我们下面只讨论其他两个方法。

1、对以上方法均不作处理,都返回super。这意味着我们既不拦截,也不消费。

大家看输出结果:

I/Activity: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN

I/MyView: dispatchTouchEvent_ACTION_DOWN

I/MyView: onTouchEvent_ACTION_DOWN

I/MyViewGroupB: onTouchEvent_ACTION_DOWN

I/MyViewGroupA: onTouchEvent_ACTION_DOWN

I/Activity: onTouchEvent_ACTION_DOWN

I/Activity: dispatchTouchEvent_ACTION_MOVE

I/Activity: onTouchEvent_ACTION_MOVE

I/Activity: dispatchTouchEvent_ACTION_UP

I/Activity: onTouchEvent_ACTION_UP

结合输出结果,我们可以总结出以下的结论:

  

结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发ACTION_DOWN的时候,事件会经过Activity——MyViewGroupA——MyViewGroupB——MyView一层层的向下进行dispatchTouchEvent(分发)—onInterceptTouchEvent(拦截)调用。当到达最底层MyView后,开始触发消费操作,因为我均不消费,ACTION_DOWN将由底层一层层向上冒,移交上层处理。当抵达最上层Activity后,说明下层均不消费,之后触发的ACTION_MOVE和ACTION_UP将不再向下层分发传递,直接交由Activity分发给自己进行处理。

2、我们将MyVIewGroupB的onInterceptTouchEvent返回值改为true,其他均是super。这意味着仅仅MyViewGroupB进行事件拦截,但均无消费

输出结果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: onTouchEvent_ACTION_DOWN

I/MyViewGroupA: onTouchEvent_ACTION_DOWN

I/Activity: onTouchEvent_ACTION_DOWN

I/Activity: dispatchTouchEvent_ACTION_MOVE

I/Activity: onTouchEvent_ACTION_MOVE

I/Activity: dispatchTouchEvent_ACTION_UP

I/Activity: onTouchEvent_ACTION_UP

结合输出结果,总结如下:

当触发ACTION_DOWN的时候,事件依然是从Activity开始一层层向下传递,当传递到MyViewGroupB时,因为进行了事件拦截,所以执行完onInterceptTouchEvent后不再向下传递,而是直接交由MyViewGroupB的onTouchEvent进行消费处理。由于我们是只拦截,不消费,所以事件向上传递,交由上层处理,最终回到Activity。之后触发的ACTION_MOVE和ACTION_UP也不再向下传递,直接交由Activity分发给自己处理。

3、我们还是将MyViewGroupB的onInterceptTouchEvent返回super,但是将他的onTouchEvent返回true。这意味着我们不拦截,但是由MyViewGroupB进行事件处理。

输出结果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN

I/MyView: dispatchTouchEvent_ACTION_DOWN

I/MyView: onTouchEvent_ACTION_DOWN

I/MyViewGroupB: onTouchEvent_ACTION_DOWN

I/Activity: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE

I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupB: onTouchEvent_ACTION_MOVE

I/Activity: dispatchTouchEvent_ACTION_UP

I/MyViewGroupA: dispatchTouchEvent_ACTION_UP

I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP

I/MyViewGroupB: dispatchTouchEvent_ACTION_UP

I/MyViewGroupB: onTouchEvent_ACTION_UP

结合输出结果,总结如下:

可以看出,当触发ACTION_DOWN的时候,事件的分发传递过程和1的时候一样,从Activity开始一层层向下传递,最终传递到最底层MyView,触发消费操作,然后MyView将消费操作移交上层处理,然后到达MyViewGroupB的onTouchEvent,并且进行了消费处理,事件处理到此不在向上移交。当触发ACTION_MOVE和ACTION_UP操作时,事件依然需要由Activity开始向下分发传递,但是当传递到MyViewGroupB后,由于其消费了ACTION_DOWN,事件将不再继续向下分发,而是直接由MyViewGroupB分发给自己的onTouchEvent进行继续处理。事件处理也不再向上移交。

4、将MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改为true。这意味着既拦截,又消费。

输出结果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN

I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN

I/MyViewGroupB: onTouchEvent_ACTION_DOWN

I/Activity: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE

I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE

I/MyViewGroupB: onTouchEvent_ACTION_MOVE

I/Activity: dispatchTouchEvent_ACTION_UP

I/MyViewGroupA: dispatchTouchEvent_ACTION_UP

I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP

I/MyViewGroupB: dispatchTouchEvent_ACTION_UP

I/MyViewGroupB: onTouchEvent_ACTION_UP

结合输出结果,总结如下:

当触发ACTION_DOWN的时候,依然从Activity开始向下传递,当到达MyViewGroupB的是,因为在onInterceptTouchEvent进行了拦截操作,因此不再继续向下分发传递,而是交由MyViewGroupB的onTouchEvent进行处理消费。MyViewGroupB的onTouchEvent返回的是true,说明它决定对ACTION_DOWN进行处理,因此事件也就不再移交上层处理。当触发ACTION_MOVE和ACTION_UP的时候,事件还是从Activity开始向下传递,当到达MyViewGroupB的时候,由于之前进行了拦截操作,因此,MyViewGroupB直接将事件分发给自己的onTouchEvent进行处理,不在向下分发传递。事件处理也不再向上层移交。

案例Demo下载地址:点击打开链接

阅读更多

【转载】Android的事件分发(dispatchTouchEvent),拦截(onInterceptTouchEvent)与处理(onTouchEvent)的更多相关文章

  1. 一文读懂 Android TouchEvent 事件分发、拦截、处理过程

    什么是事件?事件是用户触摸手机屏幕,引起的一系列TouchEvent,包括ACTION_DOWN.ACTION_MOVE.ACTION_UP.ACTION_CANCEL等,这些action组合后变成点 ...

  2. Android的事件分发

    1. Touch事件和绘制事件的异同之处 Touch事件和绘制事件非常相似,都是由ViewRoot派发下来的,可是不同之处在绘制事件是由应用中的某个View发起请求,一层一层上传到ViewRoot.再 ...

  3. Android Touch事件分发机制学习

    Android  事件分发机制 ViewGroup dispatchTouchEvent 返回true dispatchTouchEvent: Activity ACTION_DOWN Myrelat ...

  4. Android之事件分发机制

    本文主要包括以下内容 view的事件分发 viewGroup的事件分发 首先来看两张图 在执行touch事件时 首先执行dispatchTouchEvent方法,执行事件分发. 再执行onInterc ...

  5. 谈谈我对Android View事件分发的理解

    写这篇博客的缘由.近期因为项目中用到相似一个LinearLayout中水平布局中,有一个TextView和Button,然后对该LinearLayout布局设置点击事件.点击TextView能够触发该 ...

  6. Android Touch事件分发过程

    虽然网络上已经有非常多关于这个话题的优秀文章了,但还是写了这篇文章,主要还是为了加强自己的记忆吧,自己过一遍总比看别人的分析要深刻得多.那就走起吧. 简单演示样例 先看一个演示样例 : 布局文件 : ...

  7. android ontouch事件分发机制

    最近开发一个项目中,banner图左右切换和下拉刷新手势有冲突,为此去研究了事件分发,网上资料一大抄,有些讲的不对有些讲的不全,结合了网上一些博文以及源码总结如下  一个完整的触摸事件包含down,m ...

  8. android的事件分发机制理解

    android的事件分发机制理解 1.事件触发主要涉及到哪些层面的哪些函数(个人理解的顺序,可能在某一层会一次回调其它函数) activity中的dispatchTouchEvent .layout中 ...

  9. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  10. android view事件分发机制

    首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志 ...

随机推荐

  1. Chip Factory HDU - 5536 字典树(删除节点|增加节点)

    题意: t组样例,对于每一组样例第一行输入一个n,下面在输入n个数 你需要从这n个数里面找出来三个数(设为x,y,z),找出来(x+y)^z(同样也可以(y+z)^1)的最大值 ("^&qu ...

  2. C#之Dispose

    前言 谈到Dispose,首先需要理解C#的资源 资源类型 托管资源:由CLR创建和释放 非托管资源:资源的创建和释放不由CLR管理.比如IO.网络连接.数据库连接等等.需要开发人员手动释放. 如何释 ...

  3. ucosIII学习笔记——钩子函数

    一开始听见钩子函数感觉很莫名其妙,更不知道它有何作用,这是第一篇博客,也是学习ucosIII操作系统的一个开始吧. 在系统中有开发者自己创建的任务也有系统内部任务 ,UCOSIII中有五个系统任务,分 ...

  4. Leetcode(82)-删除排序链表中的重复元素 II

    给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 示例 1: 输入: 1->2->3->3->4->4->5 输出: 1-&g ...

  5. 阿里巴巴java开发手册(2020版)

    阿里巴巴java开发手册(2020版) 2020版 链接: pan.baidu.com/s/1Zls_FUBK- 密码: titz 2019版 链接: pan.baidu.com/s/1cvCVQvj ...

  6. SCSS variable for loop All In One

    SCSS variable for loop All In One @each $r: red; $g: green; $b: blue; $colors: ( 1: $r, 2: $g, 3: $b ...

  7. 如何用 js 实现一个 apply 函数

    如何用 js 实现一个 apply 函数 原理 实现方式 总结 refs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referen ...

  8. nest.js tutorials

    nest.js tutorials A progressive Node.js framework https://docs.nestjs.com//firststeps nest.js CLI ht ...

  9. c++ 获取和设置 窗口标题

    有些窗口标题中含有中文,可能识别不出来,可以改一下 SetWindowTextW GetWindowTextW 设置: SetWindowTextW( (HWND)0x00370868/*窗口句柄*/ ...

  10. 教你吃透CSS的盒子模型(Box Model)

    CSS 盒子模型(Box Model) 所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用. CSS盒模型本质上是一个盒子,封装周围的H ...