什麼是觸摸事件?顧名思義,觸摸事件就是捕獲觸摸螢幕後産生的事件。當點選一個按鈕時,通常會産生兩個或者三個事件——按鈕按下,這是事件一,如果滑動幾下,這是事件二,當手擡起,這是事件三。是以在Android中特意為觸摸事件封裝了一個類<code>MotionEvent</code>,如果重寫onTouchEvent()方法,就會發現該方法的參數就是這樣的一個<code>MotionEvent</code>,在一般重寫觸摸相關的方法中,參數一般都含有<code>MotionEvent</code>,可見它的重要性。
那麼<code>MotionEvent</code>到底是什麼東東呢,它包含了幾種類型。
Action_Down:手指剛接觸螢幕
Action_Move:手指在螢幕上移動
Action_Up:手指從螢幕上松開的一瞬間
在正常情況下,一次手指觸摸螢幕的行為會觸發一系列點選事件,考慮如下幾種情況:
點選螢幕後離開松開,事件序列為Down->Up
點選螢幕滑動一會再松開,事件序列為Down->Move->......>Move->Up
那麼,在<code>MotionEvent</code>裡面封裝了不少好東西,比如觸摸點的坐标,可以通過event.getX()方法和event.getRawX(),這兩者差別也很簡單,getX()傳回的是相對于目前View左上角的x坐标,getRawY()傳回是相對于手機螢幕左上角的x坐标,同理,y坐标也是可以擷取的,getY()和getRawY()方法,<code>MotionEvent</code>獲得點選事件的類型,可以通過不同的Action來進行區分,并實作不同的邏輯。
如此看來,觸摸事件還是簡單的,其實就是一個動作類型加坐标而已。但是我們知道,Android的View結構是樹形結構,也就是說,View可以放在ViewGroup裡面,通過不同的組合來實作不同的樣式,那麼如果View放在ViewGroup裡面,這個ViewGroup又嵌套在另一個ViewGroup裡面,甚至還有可能繼續嵌套,一層層的疊加起來呢,我們先看一個例子,是通過一個按鈕點選的。
XML檔案
Activity檔案
以上代碼很簡單,Activity中有一個LinearLayout(ViewGroup的子類,ViewGroup是View的子類)布局,布局中包含一個按鈕(View的子類),然後分别對這兩個控件設定了Touch與Click的監聽事件,具體運作結果如下:
1,當穩穩的點選Button時

2,當穩穩的點選除過Button以外的其他地方時:
3,當收指點選Button時按在Button上晃動了一下松開後
我們看下onTouch和onClick,從參數都能看出來onTouch比onClick強大靈活,畢竟多了一個event參數。這樣onTouch裡就可以處理ACTION_DOWN、ACTION_UP、ACTION_MOVE等等的各種觸摸。現在來分析下上面的列印結果;在1中,當我們點選Button時會先觸發onTouch事件(之是以列印action為0,1各一次是因為按下擡起兩個觸摸動作被觸發)然後才觸發onClick事件;在2中也同理類似1;在3中會發現onTouch被多次調運後才調運onClick,是因為手指晃動了,是以觸發了ACTION_DOWN->ACTION_MOVE…->ACTION_UP。
onTouch會有一個傳回值,而且在上面傳回了false。你可能會疑惑這個傳回值有啥效果?那就驗證一下吧,我們将上面的onTouch傳回值改為ture。如下:
顯示結果:
此時onTouch傳回true,則onClick不會被調運了。
好了,經過這個簡單的執行個體驗證你可以總結發現:
Android控件的Listener事件觸發順序是先觸發onTouch,其次onClick。
如果控件的onTouch傳回true将會阻止事件繼續傳遞,傳回false事件會繼續傳遞。
看上面的例子是不是有點困惑,為何OnTouch傳回True,onClick就不執行,事件傳遞就中斷,在這裡需要引進一個場景,這樣解釋起來就更形象生動。
首先,請想象一下生活中常見的場景:假如你所在的公司,有一個總經理,級别最高,它下面有個部長,級别次之,最底層就是幹活的你,沒有級别。現在總經理有一個任務,總經理将這個業務布置給部長,部長又把任務安排給你,當你完成這個任務時,就把任務回報給部長,部長覺得這個任務完成的不錯,于是就簽了他的名字回報給總經理,總經理看了也覺得不錯,就也簽了名字交給董事會,這樣,一個任務就順利完成了。這其實就是一個典型的事件攔截機制。
在這裡我們先定義三個類:
一個總經理—MyViewGroupA,最外層的ViewGroup
一個部長—MyViewGroupB,中間的ViewGroup
一個你—MyView,在最底層
根據以上的場景,我們可以繪制以下流程圖:
從圖中,我們可以看到在ViewGroup中,比View多了一個方法—onInterceptTouchEvent()方法,這個是幹嘛用的呢,是用來進行事件攔截的,如果被攔截,事件就不會往下傳遞了,不攔截則繼續。
如果我們稍微改動下,如果總經理(MyViewGroupA)發現這個任務太簡單,覺得自己就可以完成,完全沒必要再找下屬,是以MyViewGroupA就使用了onInterceptTouchEvent()方法把事件給攔截了,此時流程圖:
我們可以看到,事件就傳遞到MyVewGroupA這裡就不繼續傳遞下去了,就直接傳回。
如果我們再改動下,總經理(MyViewGroupA)委托給部長(MyViewGroupB),部長覺得自己就可以完成,完全沒必要再找下屬,是以MyViewGroupB就使用了onInterceptTouchEvent()方法把事件給攔截了,此時流程圖:
我們可以看到,MyViewGroupB攔截後,就不繼續傳遞了,同理如果,到幹貨的我們上(MyView),也直接傳回True的話,事件也是不會繼續傳遞的,如圖:
分析Android View事件傳遞機制之前有必要先看下源碼的一些關系,如下是幾個繼承關系圖:
看了官方這個繼承圖是不是明白了上面例子中說的LinearLayout是ViewGroup的子類,ViewGroup是View的子類,Button是View的子類關系呢?其實,在Android中所有的控件無非都是ViewGroup或者View的子類,說高尚點就是所有控件都是View的子類。
1,從View的dispatchTouchEvent方法說起
在Android中你隻要觸摸控件首先都會觸發控件的dispatchTouchEvent方法(其實這個方法一般都沒在具體的控件類中,而在他的父類View中),是以我們先來看下View的dispatchTouchEvent方法,如下:
dispatchTouchEvent的代碼有點長,但可以挑幾個重點講講,<code>if (onFilterTouchEventForSecurity(event))</code>語句判斷目前View是否沒被遮住等,然後定義ListenerInfo局部變量,ListenerInfo是View的靜态内部類,用來定義一堆關于View的XXXListener等方法;接着<code>if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))</code>語句就是重點,首先li對象自然不會為null,li.mOnTouchListener呢?你會發現ListenerInfo的mOnTouchListener成員是在哪兒指派的呢?怎麼确認他是不是null呢?通過在View類裡搜尋可以看到:
li.mOnTouchListener是不是null取決于控件(View)是否設定setOnTouchListener監聽,在上面的執行個體中我們是設定過Button的setOnTouchListener方法的,是以也不為null,接着通過位與運算确定控件(View)是不是ENABLED 的,預設控件都是ENABLED 的,接着判斷onTouch的傳回值是不是true。通過如上判斷之後如果都為true則設定預設為false的result為true,那麼接下來的<code>if (!result && onTouchEvent(event))</code>就不會執行,最終dispatchTouchEvent也會傳回true。而如果<code>if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))</code>語句有一個為false則<code>if (!result && onTouchEvent(event))</code>就會執行,如果onTouchEvent(event)傳回false則dispatchTouchEvent傳回false,否則傳回true。
這下再看前面的執行個體部分明白了吧?控件觸摸就會調運dispatchTouchEvent方法,而在dispatchTouchEvent中先執行的是onTouch方法,是以驗證了執行個體結論總結中的onTouch優先于onClick執行道理。如果控件是ENABLE且在onTouch方法裡傳回了true則dispatchTouchEvent方法也傳回true,不會再繼續往下執行;反之,onTouch傳回false則會繼續向下執行onTouchEvent方法,且dispatchTouchEvent的傳回值與onTouchEvent傳回值相同
2,dispatchTouchEvent總結
在View的觸摸屏傳遞機制中通過分析dispatchTouchEvent方法源碼我們會得出如下基本結論:
觸摸控件(View)首先執行dispatchTouchEvent方法。
在dispatchTouchEvent方法中先執行onTouch方法,後執行onClick方法(onClick方法在onTouchEvent中執行,下面會分析)。
如果控件(View)的onTouch傳回false或者mOnTouchListener為null(控件沒有設定setOnTouchListener方法)或者控件不是enable的情況下會調運onTouchEvent,dispatchTouchEvent傳回值與onTouchEvent傳回一樣。
如果控件不是enable的設定了onTouch方法也不會執行,隻能通過重寫控件的onTouchEvent方法處理(上面已經處理分析了),dispatchTouchEvent傳回值與onTouchEvent傳回一樣。
如果控件(View)是enable且onTouch傳回true情況下,dispatchTouchEvent直接傳回true,不會調用onTouchEvent方法。
3,onTouchEvent方法
首先地6到14行可以看出,如果控件(View)是disenable狀态,同時是可以clickable的則onTouchEvent直接消費事件傳回true,反之如果控件(View)是disenable狀态,同時是disclickable的則onTouchEvent直接false。多說一句,關于控件的enable或者clickable屬性可以通過java或者xml直接設定,每個view都有這些屬性。
接着22行可以看見,如果一個控件是enable且disclickable則onTouchEvent直接傳回false了;反之,如果一個控件是enable且clickable則繼續進入過于一個event的switch判斷中,然後最終onTouchEvent都傳回了true。switch的ACTION_DOWN與ACTION_MOVE都進行了一些必要的設定與置位,接着到手擡起來ACTION_UP時你會發現,首先判斷了是否按下過,同時是不是可以得到焦點,然後嘗試擷取焦點,然後判斷如果不是longPressed則通過post在UI Thread中執行一個PerformClick的Runnable,也就是performClick方法。具體如下:
這個方法也是先定義一個ListenerInfo的變量然後指派,接着判斷li.mOnClickListener是不是為null,決定執行不執行onClick。你指定現在已經很機智了,和onTouch一樣,搜一下mOnClickListener在哪指派的呗,結果發現:
控件隻要監聽了onClick方法則mOnClickListener就不為null,而且有意思的是如果調運setOnClickListener方法設定監聽且控件是disclickable的情況下預設會幫設定為clickable。
4,onTouchEvent小結
onTouchEvent方法中會在ACTION_UP分支中觸發onClick的監聽。
當dispatchTouchEvent在進行事件分發的時候,隻有前一個action傳回true,才會觸發下一個action。
通過以上總結,Android中的事件攔截機制,其實跟我們生活中的上下級委托任務很像,上司可以處理掉,也可以下發給下屬員工處理,如果員工處理的好,上司才敢給你下發任務,如果你處理不好,則上司也不敢把任務交給你,這就像在中途把下發的任務的中途攔截掉了。通過流程和源碼的分析,相信大家能比較容易了解事件的分發、攔截、處理事件的流程。在弄清楚順序機制之後,再配合源碼看,你會更加深入的了解,為什麼流程會是這樣的,最先對流程有一個大緻的認識之後,再去了解,這樣就不會一頭霧摸不着頭腦,進而會有更大的學習樂趣,畢竟在學習過程中,保持好奇心是很重要的。
源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。
1,Android系統簡介
2,ProGuard代碼混淆
3,講講Handler+Looper+MessageQueue關系
4,Android圖檔加載庫了解
5,談談Android運作時權限了解
6,EventBus初了解
7,Android 常見工具類
8,對于Fragment的一些了解
9,Android 四大元件之 " Activity "
10,Android 四大元件之" Service "
11,Android 四大元件之“ BroadcastReceiver "
12,Android 四大元件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的了解
15,Android 生命周期和啟動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工作原理
19,了解 Window 和 WindowManager
20,Activity 啟動過程分析
21,Service 啟動過程分析
22,Android 性能優化
23,Android 消息機制
24,Android Bitmap相關
25,Android 線程和線程池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸摸事件機制
29,Android 事件機制應用
30,Cordova 架構的一些了解
31,有關 Android 插件化思考
32,開發人員必備技能——單元測試