android的touch事件分发响应机制

想要弄明白android的touch事件分发响应机制需要先充分理解一下几个知识点:

  • View和ViewGroup
  • touch事件的构成
  • ViewGroup如何对事件分发和拦截
  • View和ViewGroup如何对事件进行响应

View和ViewGroup

  • 先看一下官方文档对view类的部分介绍

View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.

从这里我们就可以看出View是android中所有界面布局组件的基类,而ViewGroup 是View的一个子类,ViewGroup是一个容器类,可以往里面添加子View。

  • 再看一下android的界面是如何构成的
    这里有一篇官方文档UI Overview
    下面是该文章中的一部分内容:

All user interface elements in an Android app are built using View
and ViewGroup objects. A View is an object that draws something on the screen that the user can interact with. A ViewGroup is an object that holds other View (and ViewGroup) objects in order to define the layout of the interface.

Android provides a collection of both View and ViewGroup subclasses that offer you common input controls (such as buttons and text fields) and various layout models (such as a linear or relative layout).

User Interface Layout

上面的内容大概告诉大家android的界面的布局是一个树状的层级结构。
由顶级的ViewGroup其中包含一个或者多个View和ViewGroup来实现的。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent"    
  android:orientation="vertical">    
  <TextView        
    android:id="@+id/text"        
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"        
    android:text="I am a TextView" />    
  <Button     
    android:id="@+id/button"         
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"        
    android:text="I am a Button" />    
</LinearLayout>

这时候我们是不是得思考一下了,View和ViewGroup在界面布局的时候作用是不一样的,那么在touch事件的分发及响应上是不是也不一样呢。


touch事件的构成

在Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中的事件响应。总的来说,所有的事件都由如下三个ACTION作为基础:

  • 按下(ACTION_DOWN)
  • 移动(ACTION_MOVE)
  • 抬起(ACTION_UP)

所有的touch事件首先必须执行的是按下操作(ACTION_DOWN),之后所有的操作都是以按下操作作为前提,当按下操作完成后,接下来可能是一段移动(ACTION_MOVE)然后抬起(ACTION_UP),或者是按下操作执行完成后没有移动就直接抬起。

事件分发拦截以及响应

下面就开始本文的重点内容了:事件的分发和拦截。
说到分发和拦截,就拿上面xml中那个布局来说,Button已经是界面层级中最末端的元素了,所以它已经无法再把touch事件往下传递了,所以事件的分发和拦截其实是对ViewGroup来说的(有子view才需要把事件往下传递给子view或者拦截掉事件自己处理)。
这里主要牵涉到2个方法:

  • public boolean dispatchTouchEvent (MotionEvent event)
    从下面的方法描述中可以看出这个方法是用来分发事件的


  • public boolean onInterceptTouchEvent (MotionEvent event)
    从下面的方法描述中可以看出这个方法是用来拦截事件的


    需要注意的是onInterceptTouchEvent方法是ViewGroup的方法,View没有。

Touch事件的响应式通过下面的方法来实现的:

  • public boolean onTouchEvent (MotionEvent event)
    返回true代表自己消费了这个事件


涉及到的3个方法都讲过了,那么下面来讲一下事件分发传递及响应的流程。

  1. 事件分发流程
    从上到下,从父到子:Activity->ViewGroup1->ViewGroup1的子ViewGroup2->…->Target View
  2. 事件响应流程
    从下到上,从子到父:Target View->…->ViewGroup1的子ViewGroup2->ViewGroup1->Activity

上面是一个简单的描述,下面我们通过例子来了解详细的流程。

自定义一个ParentLayout 继承RelativeLayout;再自定义一个CustomImageView继承ImageView。

  • 对ParentLayout来说实现它的dispatchTouchEvent ,onInterceptTouchEvent以及onTouchEvent方法,并在其中打印输出接收到的事件

  • 对CustomImageView来说实现它的onTouchEvent方法,并在其中打印输出接收到的事件

界面效果图:
点击左侧汪星人的效果。


左侧的图片设置了onclick事件,所以他的touch事件是可以消费的。

16:04:53.172   D/MainActivity.dispatchTouchEvent(MainActivity.java:47)﹕ ACTION_DOWN
16:04:53.177   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:35)﹕ ACTION_DOWN
16:04:53.177   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:59)﹕ ACTION_DOWN
16:04:53.177   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.182   D/CustomImageView.onTouchEvent(CustomImageView.java:35)﹕ ACTION_DOWN
16:04:53.182   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.182   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.182   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:40)﹕ ACTION_MOVE
16:04:53.187   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:64)﹕ ACTION_MOVE
16:04:53.197   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.197   D/CustomImageView.onTouchEvent(CustomImageView.java:40)﹕ ACTION_MOVE
16:04:53.197   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.197   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.217   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:40)﹕ ACTION_MOVE
16:04:53.217   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:64)﹕ ACTION_MOVE
16:04:53.222   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.222   D/CustomImageView.onTouchEvent(CustomImageView.java:40)﹕ ACTION_MOVE
16:04:53.222   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.227   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.237   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:40)﹕ ACTION_MOVE
16:04:53.237   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:64)﹕ ACTION_MOVE
16:04:53.247   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.257   D/CustomImageView.onTouchEvent(CustomImageView.java:40)﹕ ACTION_MOVE
16:04:53.267   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.277   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.287   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:40)﹕ ACTION_MOVE
16:04:53.292   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:64)﹕ ACTION_MOVE
16:04:53.322   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.327   D/CustomImageView.onTouchEvent(CustomImageView.java:40)﹕ ACTION_MOVE
16:04:53.327   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.327   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.327   D/MainActivity.dispatchTouchEvent(MainActivity.java:51)﹕ ACTION_UP
16:04:53.332   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:43)﹕ ACTION_UP
16:04:53.332   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:67)﹕ ACTION_UP
16:04:53.332   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:04:53.337   D/CustomImageView.onTouchEvent(CustomImageView.java:43)﹕ ACTION_UP
16:04:53.337   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return true
16:04:53.337   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return true
16:04:53.342   I/MainActivity.clickImage(MainActivity.java:133)﹕ clickImage

点击左侧喵星人的效果。


右侧的喵星人没有设置事件监听,所以他没有对此次Touch事件消费,事件又向上传递回了ParentLayout的onTouchEvent。

16:12:24.882   D/MainActivity.dispatchTouchEvent(MainActivity.java:47)﹕ ACTION_DOWN
16:12:24.882   D/ParentLayout.dispatchTouchEvent(ParentLayout.java:35)﹕ ACTION_DOWN
16:12:24.882   D/ParentLayout.onInterceptTouchEvent(ParentLayout.java:59)﹕ ACTION_DOWN
16:12:24.887   E/ParentLayout.onInterceptTouchEvent(ParentLayout.java:73)﹕ return false
16:12:24.887   D/CustomImageView.onTouchEvent(CustomImageView.java:35)﹕ ACTION_DOWN
16:12:24.887   E/CustomImageView.onTouchEvent(CustomImageView.java:49)﹕ return false
16:12:24.892   D/ParentLayout.onTouchEvent(ParentLayout.java:83)﹕ ACTION_DOWN
16:12:24.892   E/ParentLayout.onTouchEvent(ParentLayout.java:97)﹕ return false
16:12:24.892   E/ParentLayout.dispatchTouchEvent(ParentLayout.java:49)﹕ return false
16:12:24.922   D/MainActivity.dispatchTouchEvent(MainActivity.java:51)﹕ ACTION_UP

TODO 此次只是分析了touch事件简单流程。后续有时间再去分析这三个方法如果返回不同的值会对事件的分发和响应有什么影响。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容