在我們平時的 Android 開發(fā)過程中,相信很多小伙伴都會遇到觸摸事件沖突這個頭疼的問題,比如說在我們的 ViewPager 中嵌套多個 Fragment,在其中某個 Fragment 中,有一個橫向滾動的廣告位,當(dāng)我們手動滑動廣告位的時候,就會遇到和 ViewPager 的滑動事件相互沖突的問題,那么怎樣解決這個問題,就是我們這篇文章所要解決的,接下來讓我們先來了解觸摸事件的類型。
一、觸摸事件的類型
1、ACTION_DOWN:用戶手指的按下操作。一個按下操作標(biāo)志著一次事件傳遞的開始。
2、ACTION_MOVE:當(dāng)手指接觸屏幕,移動超過了一定的閾值之后,就會觸發(fā) ACTION_MOVE。如果用戶手指一直沒有離開屏幕且移動的話,ACTION_MOVE 就會一直觸發(fā)。
3、ACTION_UP:當(dāng)用戶手指抬起離開屏幕的時候會觸發(fā)該ACTION_UP事件。
注意:ACTION_DOWN 和 ACTION_UP 在一次觸摸事件中,都只會觸發(fā)一次。
二、事件傳遞的三個階段
在我們具體講述 Activity、View、ViewGroup 的事件傳遞機制之前,我們先來了解一下跟事件傳遞有關(guān)的三個階段,分別是分發(fā)(dispatch)、攔截(intercept)和消費(Comsume)。而這三個階段分別對應(yīng)的方法是? dispatchTouchEvent 方法,onInterceptTouchEvent 方法和 onTouchEvent 方法。其中 onInterceptTouchEvent 方法只在 ViewGroup 及其子類才會存在,一般的 View,比如 TextView、ImageView 等非容器控件,只存在 dispatchTouchEvent 方法和 onTouchEvent 方法。
2.1、public boolean dispatchTouchEvent(MotionEvent event)
根據(jù)當(dāng)前視圖邏輯,來決定是直接消費這個事件還是將這個事件繼續(xù)傳遞給子視圖來處理。
當(dāng)返回super.dispatchTouchEvent(event) 表示繼續(xù)分發(fā)該事件。
返回 true 則表示事件被當(dāng)前視圖消費掉,不再繼續(xù)分發(fā)。后面的事件,比如 ACTION_MOVE 和 ACTION_DOWN 都會先由 Activity 的?dispatchTouchEvent 方法分發(fā),然后傳遞到該視圖的?dispatchTouchEvent 方法中進行處理。
返回 false 則事件也不會繼續(xù)傳遞給子視圖,并且當(dāng)前視圖也不會最終處理該事件,而是傳遞給父類的 onTouchEvent 方法中進行處理,直到遇到能處理該事件的視圖的 onTouchEvent 方法。
下面我們來做一個實驗,在 MainActivity 中放入一個自定義LinearLayout,名為MyLinearLayout。然后 MyLinearLayout 中包含了一個自定義 TextView,名為 MyTextView。最終生成的布局文件如圖1:

規(guī)則:默認(rèn)情況下,MainActivity、MyLinearLayout 以及 MyTextView中的事件傳遞相關(guān)方法,都是返回的super.xxx (),如果更改返回值的話,我會特別指出,除了我指出的,其余方法的返回值都是返回super.xxx ()。并且我將 ACTION_MOVE 事件的日志輸入也屏蔽掉了,避免輸出過多move日志,不便于觀察結(jié)果。
我們先將 MyLinearLayout 中的 dispatchTouchEvent 方法的返回值改為 true,打印結(jié)果為:

將 MyLinearLayout 中的?dispatchTouchEvent 方法的返回值改為 false,打印結(jié)果為:

將 MyTextView 中的?dispatchTouchEvent 方法的返回值改為 true,打印結(jié)果為:

將?MyTextView?中的?dispatchTouchEvent 方法的返回值改為 false,打印結(jié)果為:

實驗證明,跟我們上面講述的?dispatchTouchEvent 的不同返回值的表現(xiàn)結(jié)果相吻合。
2.2、public boolean onInterceptTouchEvent(MotionEvent ev)
通過返回布爾值來決定是否對事件進行攔截,返回 false 或者super.onInterceptTouchEvent 表示不對事件進行攔截,事件會繼續(xù)分發(fā)給子視圖。如果返回 true 則表示對該事件進行攔截,不繼續(xù)分發(fā)給子視圖。同時交由自身的 onTouchEvent 方法進行處理。
將 MyLinearLayout 中的?onInterceptTouchEvent?方法的返回值改為 false 或者 super.onInterceptTouchEvent,打印結(jié)果為:

將 MyLinearLayout 中的?onInterceptTouchEvent?方法的返回值改為 true,打印結(jié)果為:

結(jié)合圖6和圖7我們可以看出,當(dāng) MyLinearLayout 對事件進行攔截時,事件不會繼續(xù)傳遞給其子視圖 MyTextView 中,而是交由自身的 onTouchEvent 方法進行處理。而?onTouchEvent 方法默認(rèn)返回的是super.onTouchEvent,則會繼續(xù)交由 MainActivity 的 onTouchEvent 方法。那么,有的同學(xué)會問,如果 MyLinearLayout 的 onTouchEvent 方法返回 true 呢,會是怎樣的情況?我們修改之后再進行打印。

從圖8可以見,當(dāng) MyLinearLayout 的 onTouchEvent 方法返回 true 時,事件不會繼續(xù)向上傳遞給 MainActivity,而后續(xù)的 ACTION_UP 事件也是繼續(xù)交由 MyLinearLayout 的 onTouchEvent 方法進行處理。
2.3、public boolean onTouchEvent(MotionEvent event)
返回值為 true,表示當(dāng)前視圖可以處理該事件,事件不再繼續(xù)傳遞給父視圖進行處理。
返回值為false,表示當(dāng)前視圖不處理該事件,事件將繼續(xù)交由該視圖的父視圖進行處理。
由于在2.2中我們已經(jīng)對于 onTouchEvent 方法進行過實驗,在這里我們就不再重復(fù)實驗了。
文章介紹到此,相信大家對于 Android 觸摸事件的傳遞機制也有所了解了,在后面的實際開發(fā)中,相信大家能基于此來靈活處理一系列的手勢沖突問題。