Android成长之路-事件消费

原创 2016年08月29日 20:00:02

一、Android事件构成
Android中用户操作事件比较多,包括点击、长按、移动、拖拽等,还分单手指多手指操作等,这些都构成了Android的事件响应。所有的操作事件都由三个基础部分组成:ACTION_DOWN,ACTION_MOVE,ACTION_UP。所有的操作事件必须先执行ACTION_DOWN,以此次ACTION_DOWN为前提分析接下来的用户行为,可能是ACTION_MOVE或者是ACTION_UP, ACTION_UP是操作事件的结束,这就是一组连续的Touch操作。所有的Touch事件都是用户通过操作屏幕引起的,而展现在屏幕上可与用户交互的正是Activity及各种View,所有的事件都是在Activity和该区域内的视图之间传递和响应的。


Activity视图层级

这是Activity响应事件的层级结构,与Touch事件相关方法有三个:dispatchTouchEvent(MotionEvent ev),onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev)。dispatchTouchEvent方法用于事件的分发, 所有Android操作事件必须经过此方法来分发,然后决定是自己消费掉还是继续往下分发。onInterceptTouchEvent方法负责事件的拦截,如果拦截则不继续往下分发,否则继续分发。onTouchEvent方法负责事件的处理,它决定是否消费掉此事件。从上图可以看到,最外层的是Activity,不支持事件拦截,内层是视图容器ViewGroup,三个方法都支持,最内层的是View,由于它已经是最小的View单元,它没有子View,所有不支持事件分发和事件拦截,只有事件响应。

二、事件传递及消费
事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
Activity接收到Touch事件时,Activity的dispatchTouchEvent方法将事件传递给最外层View的dispatchTouchEvent方法,再由该View对事件继续分发。此方法返回boolean值,如果为true,则此事件会被当前View的dispatchTouchEvent方法消费掉,事件停止往下传递。如果为false,则事件会返回给它的上一层对象的onTouchEvent方法消费掉。如果返回super.dispatchTouchEvent(ev),事件会自动的分发给当前View的onInterceptTouchEvent方法。

事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
此方法返回boolean值,表示是否对事件进行拦截。
如果为true,则表示将事件进行拦截,事件由当前View的onTouchEvent方法处理。如果为false,则表示不拦截,继续向下传递给子View,由子View执行dispatchTouchEvent分发。如果返回super.onInterceptTouchEvent(ev),事件不被拦截。

事件响应:public boolean onTouchEvent(MotionEvent ev)
此方法也返回boolean值,表示是否响应该事件。
如果为true,表示接收并消费掉事件。如果为false,那么此事件会从当前View向上传递,由上层View的onTouchEvent接收,如果继续返回false,那么事件就会舍弃,而且接收不到下一次事件。如果返回 super.onTouchEvent(ev)和false逻辑相同。具体逻辑可归结如下图:


这里写图片描述

所有的操作事件都是以ACTION_DOWN开始,以ACTION_UP结束的,UP的分发取决于DOWN是否分发。

三、实例验证
如果Activity、ViewGroup和View都没有重写这三个方法,也就是View和ViewGroup都没有对ACTION_DOWN事件进行消费,之后的其他事件(MOVE和UP等)也就不会传递下去了,只会在Activity层分发和处理。见Log:

05-15 12:43:40.210: D/Activity(8409): Activity---dispatchTouchEvent: DOWN
05-15 12:43:40.210: D/ViewGroup(8409): ViewGroup---dispatchTouchEvent: DOWN
05-15 12:43:40.210: D/View(8409): View---dispatchTouchEvent: DOWN
05-15 12:43:40.210: D/View(8409): View---onTouchEvent: DOWN
05-15 12:43:40.210: D/ViewGroup(8409): ViewGroup---onTouchEvent: DOWN
05-15 12:43:40.210: D/Activity(8409): Activity---onTouchEvent: DOWN
05-15 12:43:40.240: D/Activity(8409): Activity---dispatchTouchEvent: UP
05-15 12:43:40.240: D/Activity(8409): Activity---onTouchEvent: UP

如果我们在Activity的dispatchTouchEvent()方法中将ACTION_DOWN返回true,其他ACTION返回super.dispatchTouchEvent。也就是取消事件DOWN,那么Down事件会在Activity层取消,内层的ViewGroup和View接收不到DOWN事件,当然也接收不到其他事件。在Activity层DOWN事件被dispatchTouchEvent消费掉,其他事件就交给Activity的onTouchEvent消费。见Log:

05-15 13:02:02.670: D/Activity(9924): Activity---dispatchTouchEvent: DOWN
05-15 13:02:02.690: D/Activity(9924): Activity---dispatchTouchEvent: MOVE
05-15 13:02:02.690: D/Activity(9924): Activity---onTouchEvent: MOVE
05-15 13:02:02.710: D/Activity(9924): Activity---dispatchTouchEvent: MOVE
05-15 13:02:02.710: D/Activity(9924): Activity---onTouchEvent: MOVE
05-15 13:02:02.730: D/Activity(9924): Activity---dispatchTouchEvent: MOVE
05-15 13:02:02.730: D/Activity(9924): Activity---onTouchEvent: MOVE
05-15 13:02:02.730: D/Activity(9924): Activity---dispatchTouchEvent: UP
05-15 13:02:02.730: D/Activity(9924): Activity---onTouchEvent: UP

如果Down事件在ViewGroup层的dispatchTouchEvent()方法返回true,也即取消事件停止分发,那么后续的Move事件、Up事件也只能到达ViewGroup层,并在ViewGroup层取消分发。见Log:

05-15 13:15:12.700: D/Activity(10468): Activity---dispatchTouchEvent: DOWN
05-15 13:15:12.700: D/ViewGroup(10468): ViewGroup---dispatchTouchEvent: DOWN
05-15 13:15:12.720: D/Activity(10468): Activity---dispatchTouchEvent: MOVE
05-15 13:15:12.720: D/ViewGroup(10468): ViewGroup---dispatchTouchEvent: MOVE
05-15 13:15:12.730: D/Activity(10468): Activity---dispatchTouchEvent: UP
05-15 13:15:12.730: D/ViewGroup(10468): ViewGroup---dispatchTouchEvent: UP

如果在ViewGroup层拦截事件DOWN,那么后续的Move事件、Up事件都不会被下发。如果DOWN事件在ViewGroup消费掉了,那么Move事件、Up事件也会交给ViewGroup的onTouchEvent处理,如果DOWN事件没有被ViewGroup消费,那么Move事件、Up事件就不会下发给ViewGroup。见Log:

05-15 13:41:16.395: D/Activity(14166): Activity---dispatchTouchEvent: DOWN
05-15 13:41:16.395: D/ViewGroup(14166): ViewGroup---dispatchTouchEvent: DOWN
05-15 13:41:16.395: D/ViewGroup(14166): ViewGroup---onInterceptTouchEvent: DOWN, return true.
05-15 13:41:16.395: D/ViewGroup(14166): ViewGroup---onTouchEvent: DOWN, return false.
05-15 13:41:16.395: D/Activity(14166): Activity---onTouchEvent: DOWN
05-15 13:41:16.435: D/Activity(14166): Activity---dispatchTouchEvent: MOVE
05-15 13:41:16.435: D/Activity(14166): Activity---onTouchEvent: MOVE
05-15 13:41:16.455: D/Activity(14166): Activity---dispatchTouchEvent: MOVE
05-15 13:41:16.455: D/Activity(14166): Activity---onTouchEvent: MOVE
05-15 13:41:16.455: D/Activity(14166): Activity---dispatchTouchEvent: UP
05-15 13:41:16.455: D/Activity(14166): Activity---onTouchEvent: UP

如果DOWN事件分发到最内层的View,并且被View消费,即onTouchEvent返回true,那么MOVE和UP事件也会下发给View,是否消费就由View自己决定,如果不消费则向上传递。

05-15 14:04:08.095: D/Activity(14579): Activity---dispatchTouchEvent: DOWN
05-15 14:04:08.095: D/ViewGroup(14579): ViewGroup---dispatchTouchEvent: DOWN
05-15 14:04:08.095: D/View(14579): View---dispatchTouchEvent: DOWN
05-15 14:04:08.095: D/View(14579): View---onTouchEvent: DOWN, return true。
05-15 14:04:08.135: D/Activity(14579): Activity---dispatchTouchEvent: UP
05-15 14:04:08.135: D/ViewGroup(14579): ViewGroup---dispatchTouchEvent: UP
05-15 14:04:08.135: D/View(14579): View---dispatchTouchEvent: UP
05-15 14:04:08.135: D/View(14579): View---onTouchEvent: UP, return true。

四、总结
事件分发按照从外到内进行层级传递,Activity – ViewGroup – View
事件消费按照从内到外进行层级上传,如果onTouchEvent返回true,则事件在该层消费掉。

【Android成长之路】最常用和最难用的控件——ListView的浅谈(ListView 的点击事件)

如果单单只是一个显示东东的ListView,是不是还不够Cool,你是不是还想在上面折腾点什么?还有什么比点击事件更酷( ▼-▼)...

Android成长之路-布局详解

布局详解:       1、线性布局 LinearLayout  线性布局                     一个布局,组织成一个单一的水平或垂直行其子。             ...

我的Android成长之路(6)----利用elevation设置控件的高度(不是height)

Android L: Google已经确认Android L就是Android Lollipop(5.0)。 Google之前就已经提前推出了Android L Developer...
  • cuper_
  • cuper_
  • 2016年11月05日 10:20
  • 2432

Android&java的成长之路之六(2048小游戏②)

紧接着上一章,我们说到了要把卡片部署到GridLayout上。我们玩正规的2048游戏的时候,看上去是卡片移动来叠加数字,实则不然,它只是添加了动画效果,其实是之前已经全部铺满地卡片,只是能够重叠的时...

Android 蓝牙BLE (蓝牙成长之路)4

其实前面的除了service大家最关注的的 可能还是 中间设备和外围设备他们之间的连接问题(PS:这也是我最关心的问题,本人曾经在百度知道里面花重财富,结果有人回答,但是他的回答,不过是百度,粘贴复制...

Android 蓝牙BLE (蓝牙成长之路)2

接下来  要做的 就是扫描 BLE设备了  // 扫描BLE设备 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @SuppressLint("Ne...

一枚Android小狮子的成长之路(前言)

一直以来,总想着把自己的学习和工作种学到的内容总结一下,写一篇连续的博客,来记录Android学习和工作中的点点滴滴。回想这么几年的工作和学习历程,其实学习到的东西也挺多的,也挺繁杂的。很多时候不是想...

【Android成长之路】Toast的简单应用

Toast 是Android 系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。...

【Android成长之路】全局大喇叭——广播机制的浅谈(使用本地广播)

关于安卓引入的本地广播机制的简单讲解。

Android成长之路-HelloWorld项目的创建

首先创建HelloWorld项目     进入下面这个菜单中         选择Android Project 点击next进入到创建窗口         在P...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android成长之路-事件消费
举报原因:
原因补充:

(最多只允许输入30个字)