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事件都会只走到这一层后,就开始停止继续向下走“隧道“,而是从当前层开始“冒泡“。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值