之前也自己學(xué)習(xí)過,但是當(dāng)時(shí)學(xué)的不是很透徹而且有點(diǎn)忘了,重新總結(jié)一下。
首先,需要說一下,Android中的基本觸屏事件主要有:按下、滑動(dòng)、抬起。
分別對(duì)應(yīng)的是MotionEvent類中的ACTION_DOWN,ACTION_MOVE,ACTION_UP。
而一個(gè)展示在我們面前的Activity大致有這么幾層:
Activity -> PhoneWindow -> DecorView -> ViewGroup -> View
也就是說每個(gè)Activity都有一個(gè)Window對(duì)象,Window是一個(gè)抽象類,一般實(shí)現(xiàn)的是PhoneWindow,它會(huì)包含一個(gè)DecorView,這個(gè)就是整個(gè)窗口的根View,它其中的內(nèi)容就是我們?cè)诓季治募袑懙膬?nèi)容了。我們的布局文件都是在ViewGroup中包含一些控件,這就組成了一個(gè)Activity。
我們點(diǎn)擊一下屏幕就產(chǎn)生一個(gè)事件,首先是最上層的Activity收到這個(gè)事件,它決定是否向下分發(fā),我們一般操作的是從自定義ViewGroup開始的,因?yàn)樵诙嗫丶丿B的時(shí)候如果處理不好點(diǎn)擊事件的處理問題,就會(huì)導(dǎo)致一些控件無法響應(yīng)。
事件的攔截與分發(fā)和處理主要由三個(gè)方法決定
順序也是:分發(fā)-攔截-處理消費(fèi)
-
dispatchTouchEvent
事件分發(fā),決定是否分發(fā)當(dāng)前事件,如果不分發(fā),也就表示事件到此之后不會(huì)往下走, 那就沒有然后了。一般來講不會(huì)改寫這個(gè)方法,因?yàn)锳ctivity、ViewGroup、View都有這個(gè) 方法,一旦不分發(fā)(返回true)結(jié)果只有一個(gè),就是觸屏事件沒有消費(fèi)者了,那么點(diǎn)擊事件就等于沒有了。 -
onInterceptTouchEvent(只有ViewGroup中才有這個(gè)方法)
這個(gè)是一個(gè)很重要的方法,表示了是否攔截觸屏事件。如果攔截,那么由于已經(jīng)執(zhí)行過事件分發(fā),那這個(gè)事件就會(huì)傳遞到當(dāng)前ViewGroup的onTouchEvent方法,由它進(jìn)行事件處理,處理結(jié)束后返回上層的ViewGroup或Activity的onTouchEvent,其下層的View就沒什么事了。如果不攔截,那么就會(huì)繼續(xù)往下傳遞。 -
onTouchEvent
一般在這個(gè)方法中處理觸屏事件。處理完畢后默認(rèn)向上返回。
下面通過一些例子來深刻認(rèn)識(shí)一下,有一點(diǎn)要提的是,dispatchTouchEvent返回true表示消費(fèi)了事件,就不進(jìn)行分發(fā);onInterceptTouchEvent返回true表示進(jìn)行攔截,不繼續(xù)傳遞;onTouchEvent返回true表示消費(fèi)了事件,不用返回了。
首先是我的布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main3"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.gsq.viewtest01.Main3Activity">
<com.example.gsq.viewtest01.viewgroup.ViewGroupTest01
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000" >
<com.example.gsq.viewtest01.viewgroup.ViewGroupTest02
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#0000ff">
<com.example.gsq.viewtest01.view.ViewTouchTest
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#00ff00" />
</com.example.gsq.viewtest01.viewgroup.ViewGroupTest02>
</com.example.gsq.viewtest01.viewgroup.ViewGroupTest01>
</RelativeLayout>
兩個(gè)ViewGroup包了一個(gè)View。
大概這樣:

在默認(rèn)的情況下,點(diǎn)擊一下綠色區(qū)域(由于按下與抬起是兩個(gè)事件,測(cè)試中只分析按下事件):

這么來看的話,默認(rèn)情況下,來一個(gè)事件,首先是dispatchTouchEvent進(jìn)行分發(fā),然后onInterceptTouchEvent判斷是否攔截,不攔截就繼續(xù)往下,也就是上層不斷的往下層分發(fā),一路不攔截,直到不能繼續(xù)向下,然后最底層的View的onTouchEvent處理事件,再一路向上執(zhí)行onTouchEvent。
如果ViewGroup1攔截了事件(onInterceptTouchEvent返回true):

果然,分發(fā)了之后,進(jìn)行攔截的話表示就不向下層繼續(xù)傳遞了,那么就是當(dāng)前ViewGroup1的onTouchEvent處理,處理結(jié)束后向上返回。
那如果在onTouchEvent中消費(fèi)了事件呢?
將View中的onTouchEvent返回true:

果然,onTouchEvent返回true,表示這個(gè)事件我消費(fèi)完了,沒了。
那么就不用再向上層傳遞了。同理,如果ViewGroup2的onTouchEvent返回true,那就表示ViewGroup2把事件消費(fèi)了,事件沒了,不用往上傳了。

上面的測(cè)試都是點(diǎn)擊的綠色區(qū)域,當(dāng)然了,如果點(diǎn)擊的是藍(lán)色區(qū)域,那么就是ViewGroup2在最底層,處理方式還是相同的。
后面自己自定義控件或布局重疊的時(shí)候遇到問題再回來補(bǔ)充。