根據以下文章總結
原文出處:https://blog.csdn.net/github_26939093/article/details/51124443
文章推薦:https://blog.csdn.net/morgan_xww/article/details/9372285/
跟touch事件相關的3個方法:
public boolean dispatchTouchEvent(MotionEvent ev); //用來分派event
public boolean onInterceptTouchEvent(MotionEvent ev); //用來攔截event
public boolean onTouchEvent(MotionEvent ev); //用來處理event
擁有這三個方法的類:
Activity類 | dispatchTouchEvent,onTouchEvent |
---|---|
View容器類 | dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent |
View控件 | dispatchTouchEvent,onTouchEvent |
android view事件傳遞機制對不管是初學者還是有經驗的開發者來說,都是一個比較核心的機制。可能說起事件分發機制,很多人都能立馬就能說出dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()這三個回調方法,有些人甚至可能也會說出“隧道“,“冒泡“機制。誠然,android 的view事件分發機制確實離不開這三個方法,但是對於這三個方法之間是如何分發,攔截,相互協調工作的,怎樣“隧道“,“冒泡“以及每個方法中返回的布爾值對觸摸事件造成的影響,相信很多人理解的可能還不是十分透徹。
本篇文章就將通過在一個三層的view型樹結構上觸發手勢觸摸事件實驗,通過不斷修改這三個方法中的返回值,來深入理解android 中 view的事件分發,攔截機制。
該實驗中將用到四個類文件,分別是:
TestViewEventActivity :用來展示三層view佈局文件,以及接收用戶觸摸事件的Activity
OuterLayout 第一層,繼承自RelativeLayout
InngerLayout 第二層view,繼承自RelativeLayout
LeafView 第三層view ,繼承子TextView
運行起來後,效果圖展示如下:
先來看TestViewEventActivity的源代碼:
package love.gaoge.view.event;
import android.os.Bundle;
import android.view.MotionEvent;
import love.gaoge.R;
import love.gaoge.base.BaseActivity;
import love.gaoge.util.Logg;
/**
* 測試父view,子view touch事件
* @author gaoge
* @version V1.0
* @date 2016-03-02 18:20
* @tips
*/
public class TestViewEventActivity extends BaseActivity{
String tag = "eve";
private OuterLayout outer;
private InnerLayout inner;
private LeafView leaf;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_view_event);
outer = (OuterLayout)findViewById(R.id.outer);
inner = (InnerLayout)findViewById(R.id.inner);
leaf = (LeafView)findViewById(R.id.leaf);
/**
* 只有設置了clickListener以後,纔可以監聽到ACTION_MOVE,ACTION_UP事件!!!
*/
// outer.setOnClickListener(this);
// inner.setOnClickListener(this);
// leaf.setOnClickListener(this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "TestViewEvent.dispatchTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "TestViewEvent.dispatchTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "TestViewEvent.dispatchTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "TestViewEvent.dispatchTouchEvent(),ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "TestViewEvent.onTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "TestViewEvent.onTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "TestViewEvent.onTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "TestViewEvent.onTouchEvent(),ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
OuterLayout源代碼:
package love.gaoge.view.event;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import love.gaoge.util.Logg;
/**
* @author gaoge
* @version V1.0
* @date 2016-03-02 18:18
* @tips
*/
public class OuterLayout extends RelativeLayout {
String tag = "eve";
public OuterLayout(Context context) {
super(context);
}
public OuterLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OuterLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "OuterLayout.dispatchTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "OuterLayout.dispatchTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "OuterLayout.dispatchTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "OuterLayout.dispatchTouchEvent(),ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "OuterLayout.onInterceptTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "OuterLayout.onInterceptTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "OuterLayout.onInterceptTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "OuterLayout.onInterceptTouchEvent(),ACTION_CANCEL");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "OuterLayout.onTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "OuterLayout.onTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "OuterLayout.onTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "OuterLayout.onTouchEvent(),ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
InnerLayout源代碼:
package love.gaoge.view.event;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import love.gaoge.util.Logg;
/**
* @author gaoge
* @version V1.0
* @date 2016-03-02 18:19
* @tips
*/
public class InnerLayout extends RelativeLayout {
String tag = "eve";
public InnerLayout(Context context) {
super(context);
}
public InnerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InnerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "InnerLayout.dispatchTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "InnerLayout.dispatchTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "InnerLayout.dispatchTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "InnerLayout.dispatchTouchEvent(),ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "InnerLayout.onInterceptTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "InnerLayout.onInterceptTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "InnerLayout.onInterceptTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "InnerLayout.onInterceptTouchEvent(),ACTION_CANCEL");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// getParent().requestDisallowInterceptTouchEvent(true);
Logg.d(tag, "InnerLayout.onTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
// getParent().requestDisallowInterceptTouchEvent(false);
Logg.d(tag, "InnerLayout.onTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "InnerLayout.onTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "InnerLayout.onTouchEvent(),ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
LeafView源代碼:
package love.gaoge.view.event;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
import love.gaoge.util.Logg;
/**
* @author gaoge
* @version V1.0
* @date 2016-03-02 18:19
* @tips
*/
public class LeafView extends TextView {
String tag = "eve";
public LeafView(Context context) {
super(context);
}
public LeafView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LeafView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Logg.d(tag, "LeafView.dispatchTouchEvent(),ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "LeafView.dispatchTouchEvent(),ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "LeafView.dispatchTouchEvent(),ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "LeafView.dispatchTouchEvent(),ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// getParent().requestDisallowInterceptTouchEvent(true);
Logg.d(tag, "LeafView.onTouchEvent(),ACTION_DOWN");
// return true;
break;
case MotionEvent.ACTION_MOVE:
Logg.d(tag, "LeafView.onTouchEvent(),ACTION_MOVE");
// return false;
break;
case MotionEvent.ACTION_UP:
Logg.d(tag, "LeafView.onTouchEvent(),ACTION_UP");
// return false;
break;
case MotionEvent.ACTION_CANCEL:
Logg.d(tag, "LeafView.onTouchEvent(),ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
可以看到,在這幾個類的事件相關的方法中,我們都指定打印出一些log信息,來標記事件的執行流程:
實驗一:
TestViewEvent,OuterLayout,InnerLayout,LeafView 的事件相關的方法默認都返回
super.xxx()(即觸摸事件中間沒有被任何一個View給攔截,消耗掉),這時手指在LeafView區域滑動,打印出來的log信息展示如下:
D/eve (25404): TestViewEvent.dispatchTouchEvent(),ACTION_DOWN
D/eve (25404): OuterLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve (25404): OuterLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve (25404): InnerLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve (25404): InnerLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve (25404): LeafView.dispatchTouchEvent(),ACTION_DOWN
D/eve (25404): LeafView.onTouchEvent(),ACTION_DOWN
D/eve (25404): InnerLayout.onTouchEvent(),ACTION_DOWN
D/eve (25404): OuterLayout.onTouchEvent(),ACTION_DOWN
D/eve (25404): TestViewEvent.onTouchEvent(),ACTION_DOWN
D/eve (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve (25404): TestViewEvent.dispatchTouchEvent(),ACTION_UP
D/eve (25404): TestViewEvent.onTouchEvent(),ACTION_UP
通過log信息可以看到,
先是ACTION_DOWN事件在三成view樹中進行向下傳遞,傳遞的順序依次是:
TestViewEventActivity.dispatchTouchEvent()
OuterLayout.dispatchTouchEvent(), OuterLayout.onInterceptTouchEvent()
InnerLayout.dispatchTouchEvent(),InnerLayout.onInterceptTouchEvent()
LeafView.dispatchTouchEvent()
這是否是像ACTION_DOWN事件在從上往下挖”隧道”?從最上層的TestViewEventActivity ,一直走到了最下層LeafView的dispatchTouchEvent()方法。
接着接着看log,ACTION_DOWN事件又經歷了一下方法:
LeafView.onTouchEvent()
InnerLayout.onTouchEvent()
OuterLayout.onTouchEvent()
TestViewEvent.onTouchEvent()
這是否像ACTION_DOWN事件經歷了一次“冒泡“過程,從最下層的LeafView,一直到最上層的TestViewEventActivity.onTouchEvent()方法。所以對於觸摸事件中的ACTION_DOWN事件,分別經歷了一個“隧道“,然後“冒泡“的過程。但是Android中的一次完整觸摸事件,是包括了ACTION_DOWN,ACTION_MOVE,ACTION_UP事件的,接着看ACTION_MOVE事件,並沒有像ACTION_DOWN事件那樣經歷“隧道“,“冒泡“過程,而是直接走TestViewEvent.dispatchTouchEvent(),onTouchEvent()方法,對於這次觸摸事件的ACTION_UP,也是如此。這又是爲什麼呢?其實是這樣的,對於這次觸摸事件,因爲所有的view(包括OuterLayout,InnerLayout,LeafView)都沒有對ACTION_DOWN事件進行消耗(即在各自的onTouchEvent()中返回true),那麼android系統就認爲沒有view對這次手勢觸摸事件感興趣,那麼以後的ACTION_MOVE,ACTION_UP事件就不再向下傳遞,而是直接由TestViewEventActivity的onTouchEvent()來處理了。可能有同學對這個推論有懷疑,那我們接着來驗證我們的這個判斷。
實驗二:
只將LeafView的onTouchEvent()中返回true,其他變量保持不變。看看這時候觸摸事件的執行過程是什麼樣的。
D/eve (26971): TestViewEvent.dispatchTouchEvent(),ACTION_DOWN
D/eve (26971): OuterLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve (26971): OuterLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve (26971): InnerLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve (26971): InnerLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve (26971): LeafView.dispatchTouchEvent(),ACTION_DOWN
D/eve (26971): LeafView.onTouchEvent(),ACTION_DOWN
D/eve (26971): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve (26971): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): InnerLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve (26971): LeafView.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): LeafView.onTouchEvent(),ACTION_MOVE
D/eve (26971): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve (26971): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): InnerLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve (26971): LeafView.dispatchTouchEvent(),ACTION_MOVE
D/eve (26971): LeafView.onTouchEvent(),ACTION_MOVE
D/eve (26971): TestViewEvent.dispatchTouchEvent(),ACTION_UP
D/eve (26971): OuterLayout.dispatchTouchEvent(),ACTION_UP
D/eve (26971): OuterLayout.onInterceptTouchEvent(),ACTION_UP
D/eve (26971): InnerLayout.dispatchTouchEvent(),ACTION_UP
D/eve (26971): InnerLayout.onInterceptTouchEvent(),ACTION_UP
D/eve (26971): LeafView.dispatchTouchEvent(),ACTION_UP
D/eve (26971): LeafView.onTouchEvent(),ACTION_UP
首先我們來看ACTION_DOWN事件,同之前那次測試一樣,ACTION_DOWN事件還是先執行了一次“隧道“,從最上層的TestViewEventActivity一直走到了最下層的LeafView,但是不同的是LeafView的onTouchEvent()返回了true,說明LeafView對這次手勢觸摸事件感興趣,那麼ACTION_DOWN事件在這時候便終止了繼續向上冒泡,緊接着的ACTION_MOVE,ACTION_UP事件都和ACTION_DOWN事件一樣,經歷“隧道“來到LeafView後,便停止繼續向上“冒泡“。這個實驗說明了一個問題,即冒泡過程是可以被終止的,當有view消耗掉ACTION_DOWN事件時,冒泡過程便即可中止,以後的ACTION_MOVE,ACTION_UP事件也將交由該view來處理。
那既然“冒泡“過程可以被終止,“隧道“過程可以被終止嗎?我們再來實驗下。
實驗三:
在LeafView的onTouchEvent()返回true的基礎上,讓InnerLayout的onTouchEvent()也返回true.這時候log信息如下:
D/eve ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve ( 1917): LeafView.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 1917): LeafView.onTouchEvent(),ACTION_DOWN
D/eve ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 1917): LeafView.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): LeafView.onTouchEvent(),ACTION_MOVE
D/eve ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 1917): LeafView.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 1917): LeafView.onTouchEvent(),ACTION_MOVE
D/eve ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_UP
D/eve ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_UP
D/eve ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_UP
D/eve ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_UP
D/eve ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_UP
D/eve ( 1917): LeafView.dispatchTouchEvent(),ACTION_UP
D/eve ( 1917): LeafView.onTouchEvent(),ACTION_UP
可以看到,即使InnerLayout的onTouchEvent()方法返回了true,ACTION_DOWN事件也是會一直走“隧道“到最下層的LeafView,然後判斷LeafView的onTouchEvent()方法,如過該方法返回true,那麼以後的ACTION_MOVE,ACTION_UP事件依舊只會交給LeafView的onTouchEvent()方法來處理。這也就解釋了爲什麼在child view 和 parent view上都設點擊事件的時候,在child view上點擊,能觸發的永遠是child view的onClick()事件,而不是parent view的。
好的,根據這個原理,我們來猜測下,如果LeafView的onTouchEvent()返回false,InnerLayout的onTouchEvent()返回true,那麼執行流程應該是ACTION_DOWN事件首先走隧道到LeafView的onTouchEvent()方法,看到返回false,然後冒泡到InnerLayout的onTouchEvent()方法,返回true,說明InnerLayout對這次觸摸事件感興趣,所以以後的ACTION_MOVE,ACTION_UP事件就會都交由InnerLayout的onTouchEvent()方法來處理,而不會再繼續走“隧道“到LeafView的onTouchEvent()方法,那究竟是不是這樣呢?我們再做給實驗:
實驗三:
LeafView onTouchEvent()返回fasle,InnerLayout onTouchEvent()返回true.
log信息如下:
D/eve ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 3673): InnerLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve ( 3673): LeafView.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 3673): LeafView.onTouchEvent(),ACTION_DOWN
D/eve ( 3673): InnerLayout.onTouchEvent(),ACTION_DOWN
D/eve ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): InnerLayout.onTouchEvent(),ACTION_MOVE
D/eve ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_MOVE
D/eve ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 3673): InnerLayout.onTouchEvent(),ACTION_MOVE
D/eve ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_UP
D/eve ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_UP
D/eve ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_UP
D/eve ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_UP
D/eve ( 3673): InnerLayout.onTouchEvent(),ACTION_UP
根據log信息,也印證了我們之前的判斷.所以對於一次手勢觸摸事件,ACTION_DOWN事件就像一個標誌,如果ACTION_DOWN事件被誰消耗掉了,那麼在事件不被攔截的情況下,之後所有的該觸摸事件的ACTION_MOVE,ACTION_UP事件都會直接找到該view,並將ACTION_MOVE,ACTION_UP事件交由該view處理。
好,接下來我們再看看onInterceptTouchEvent()方法的作用。
實驗四:
接着實驗三的前提條件,即InnerLayout的onTouchEvent()返回true,LeafView的onTouchEvent()返回fasle,將OuterLayout的onInterceptTouchEvent()的方法返回true,表示OuterLayout對這次觸摸事件進行攔截,log信息如下:
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 5388): OuterLayout.dispatchTouchEvent(),ACTION_DOWN
D/eve ( 5388): OuterLayout.onInterceptTouchEvent(),ACTION_DOWN
D/eve ( 5388): OuterLayout.onTouchEvent(),ACTION_DOWN
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_DOWN
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVE
D/eve ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_UP
D/eve ( 5388): TestViewEvent.onTouchEvent(),ACTION_UP
可以看到ACTON_DOWN事件在走到OuterLayout後,就不再繼續向下走“隧道“了,而是直接調用自己的onTouchEvent()方法,因爲該方法返回false,說明對這次觸摸事件不感興趣,則以後的ACTION_MOVE,ACTION_UP事件就交給最上層的TestViewEventActivity的相關方法進行處理。所以可以看出,onInterceptTouchEvent()其實是可以對“隧道“過程進行中斷的,即指定觸摸事件在走到某一層的時候就立刻返回執行“冒泡“。
總結:
1:一次觸摸事件按照事件發生順序包含了ACTION_DOWN,ACTION_MOVE,ACTION_UP事件,首先是ACTION_DOWN事件走“隧道“,中間在沒有被onInterceptTouchEvent()給攔截掉的情況下,會一直走到最下層view的dispatchTouchEvent()方法,然後開始“冒泡“,“冒泡“過程和“隧道“過程不太一樣,在“冒泡“過程中,一旦找到了將ACTION_DOWN事件消耗掉的view,那麼之後的ACTION_MOVE,ACTION_UP事件就相當於找到了targetView,會在走隧道的過程中,只走到targetView所在的那一層(不一定像ACTION_DOWN事件那樣一直走到最底層),並且將ACTION_MOVE,ACTION_UP事件交由該targetView進行處理。
2:onInterceptTouchEvent()方法,可以改變觸摸事件中走“隧道“過程的深度,如果某一層view的onInterceptTouchEvent()方法返回了true,那麼包括最開始的ACTION_DOWN,以及緊接着之後的ACTION_MOVE,ACTION_UP事件都會只走到這一層後,就開始停止繼續向下走“隧道“,而是從當前層開始“冒泡“。