View的Touch事件-隧道/冒泡原理分析

根據以下文章總結
原文出處: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事件都會只走到這一層後,就開始停止繼續向下走“隧道“,而是從當前層開始“冒泡“。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章