想要弄明白android的touch事件分發(fā)響應(yīng)機(jī)制需要先充分理解一下幾個(gè)知識(shí)點(diǎn):
- View和ViewGroup
- touch事件的構(gòu)成
- ViewGroup如何對(duì)事件分發(fā)和攔截
- View和ViewGroup如何對(duì)事件進(jìn)行響應(yīng)
View和ViewGroup
- 先看一下官方文檔對(duì)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的一個(gè)子類,ViewGroup是一個(gè)容器類巾表,可以往里面添加子View汁掠。
- 再看一下android的界面是如何構(gòu)成的
這里有一篇官方文檔UI Overview
下面是該文章中的一部分內(nèi)容:
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).
上面的內(nèi)容大概告訴大家android的界面的布局是一個(gè)樹(shù)狀的層級(jí)結(jié)構(gòu)摩窃。
由頂級(jí)的ViewGroup其中包含一個(gè)或者多個(gè)View和ViewGroup來(lái)實(shí)現(xiàn)的隆敢。
<?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>
這時(shí)候我們是不是得思考一下了,View和ViewGroup在界面布局的時(shí)候作用是不一樣的粘姜,那么在touch事件的分發(fā)及響應(yīng)上是不是也不一樣呢鞠苟。
touch事件的構(gòu)成
在Android中乞榨,事件主要包括點(diǎn)按秽之、長(zhǎng)按、拖拽吃既、滑動(dòng)等考榨,點(diǎn)按又包括單擊和雙擊,另外還包括單指操作和多指操作鹦倚。所有這些都構(gòu)成了Android中的事件響應(yīng)河质。總的來(lái)說(shuō)震叙,所有的事件都由如下三個(gè)ACTION作為基礎(chǔ):
- 按下(ACTION_DOWN)
- 移動(dòng)(ACTION_MOVE)
- 抬起(ACTION_UP)
所有的touch事件首先必須執(zhí)行的是按下操作(ACTION_DOWN)掀鹅,之后所有的操作都是以按下操作作為前提,當(dāng)按下操作完成后捐友,接下來(lái)可能是一段移動(dòng)(ACTION_MOVE)然后抬起(ACTION_UP)淫半,或者是按下操作執(zhí)行完成后沒(méi)有移動(dòng)就直接抬起溃槐。
事件分發(fā)攔截以及響應(yīng)
下面就開(kāi)始本文的重點(diǎn)內(nèi)容了:事件的分發(fā)和攔截匣砖。
說(shuō)到分發(fā)和攔截,就拿上面xml中那個(gè)布局來(lái)說(shuō)昏滴,Button已經(jīng)是界面層級(jí)中最末端的元素了猴鲫,所以它已經(jīng)無(wú)法再把touch事件往下傳遞了,所以事件的分發(fā)和攔截其實(shí)是對(duì)ViewGroup來(lái)說(shuō)的(有子view才需要把事件往下傳遞給子view或者攔截掉事件自己處理)谣殊。
這里主要牽涉到2個(gè)方法:
-
public boolean dispatchTouchEvent (MotionEvent event)
從下面的方法描述中可以看出這個(gè)方法是用來(lái)分發(fā)事件的
-
public boolean onInterceptTouchEvent (MotionEvent event)
從下面的方法描述中可以看出這個(gè)方法是用來(lái)攔截事件的
需要注意的是onInterceptTouchEvent方法是ViewGroup的方法拂共,View沒(méi)有。
Touch事件的響應(yīng)式通過(guò)下面的方法來(lái)實(shí)現(xiàn)的:
-
public boolean onTouchEvent (MotionEvent event)
返回true代表自己消費(fèi)了這個(gè)事件
涉及到的3個(gè)方法都講過(guò)了姻几,那么下面來(lái)講一下事件分發(fā)傳遞及響應(yīng)的流程宜狐。
-
事件分發(fā)流程
從上到下,從父到子:Activity->ViewGroup1->ViewGroup1的子ViewGroup2->…->Target View -
事件響應(yīng)流程
從下到上蛇捌,從子到父:Target View->…->ViewGroup1的子ViewGroup2->ViewGroup1->Activity
上面是一個(gè)簡(jiǎn)單的描述抚恒,下面我們通過(guò)例子來(lái)了解詳細(xì)的流程。
自定義一個(gè)ParentLayout 繼承RelativeLayout络拌;再自定義一個(gè)CustomImageView繼承ImageView俭驮。
對(duì)ParentLayout來(lái)說(shuō)實(shí)現(xiàn)它的dispatchTouchEvent ,onInterceptTouchEvent以及onTouchEvent方法春贸,并在其中打印輸出接收到的事件
對(duì)CustomImageView來(lái)說(shuō)實(shí)現(xiàn)它的onTouchEvent方法混萝,并在其中打印輸出接收到的事件
界面效果圖:
點(diǎn)擊左側(cè)汪星人的效果。
左側(cè)的圖片設(shè)置了onclick事件萍恕,所以他的touch事件是可以消費(fèi)的逸嘀。
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
點(diǎn)擊左側(cè)喵星人的效果。
右側(cè)的喵星人沒(méi)有設(shè)置事件監(jiān)聽(tīng)允粤,所以他沒(méi)有對(duì)此次Touch事件消費(fèi)崭倘,事件又向上傳遞回了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事件簡(jiǎn)單流程。后續(xù)有時(shí)間再去分析這三個(gè)方法如果返回不同的值會(huì)對(duì)事件的分發(fā)和響應(yīng)有什么影響绳姨。