彻底了解android view 事件机制中的“隧道”和“冒泡”

来源:互联网 发布:android手机投影到mac 编辑:程序博客网 时间:2024/05/02 05:06

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_DOWND/eve     (25404): OuterLayout.dispatchTouchEvent(),ACTION_DOWND/eve     (25404): OuterLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     (25404): InnerLayout.dispatchTouchEvent(),ACTION_DOWND/eve     (25404): InnerLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     (25404): LeafView.dispatchTouchEvent(),ACTION_DOWND/eve     (25404): LeafView.onTouchEvent(),ACTION_DOWND/eve     (25404): InnerLayout.onTouchEvent(),ACTION_DOWND/eve     (25404): OuterLayout.onTouchEvent(),ACTION_DOWND/eve     (25404): TestViewEvent.onTouchEvent(),ACTION_DOWND/eve     (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     (25404): TestViewEvent.dispatchTouchEvent(),ACTION_UPD/eve     (25404): TestViewEvent.onTouchEvent(),ACTION_UP

通过log信息可以看到,

先是ACTION_DOWN事件在三成view树中进行向下传递,传递的顺序依次是:

  1. TestViewEventActivity.dispatchTouchEvent()
  2. OuterLayout.dispatchTouchEvent(), OuterLayout.onInterceptTouchEvent()
  3. InnerLayout.dispatchTouchEvent(),InnerLayout.onInterceptTouchEvent()
  4. LeafView.dispatchTouchEvent()

这是否是像ACTION_DOWN事件在从上往下挖”隧道”?从最上层的TestViewEventActivity ,一直走到了最下层LeafView的dispatchTouchEvent()方法。

接着接着看log,ACTION_DOWN事件又经历了一下方法:

  1. LeafView.onTouchEvent()
  2. InnerLayout.onTouchEvent()
  3. OuterLayout.onTouchEvent()
  4. 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_DOWND/eve     (26971): OuterLayout.dispatchTouchEvent(),ACTION_DOWND/eve     (26971): OuterLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     (26971): InnerLayout.dispatchTouchEvent(),ACTION_DOWND/eve     (26971): InnerLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     (26971): LeafView.dispatchTouchEvent(),ACTION_DOWND/eve     (26971): LeafView.onTouchEvent(),ACTION_DOWND/eve     (26971): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     (26971): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): InnerLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     (26971): LeafView.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): LeafView.onTouchEvent(),ACTION_MOVED/eve     (26971): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     (26971): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): InnerLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     (26971): LeafView.dispatchTouchEvent(),ACTION_MOVED/eve     (26971): LeafView.onTouchEvent(),ACTION_MOVED/eve     (26971): TestViewEvent.dispatchTouchEvent(),ACTION_UPD/eve     (26971): OuterLayout.dispatchTouchEvent(),ACTION_UPD/eve     (26971): OuterLayout.onInterceptTouchEvent(),ACTION_UPD/eve     (26971): InnerLayout.dispatchTouchEvent(),ACTION_UPD/eve     (26971): InnerLayout.onInterceptTouchEvent(),ACTION_UPD/eve     (26971): LeafView.dispatchTouchEvent(),ACTION_UPD/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_DOWND/eve     ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_DOWND/eve     ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_DOWND/eve     ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     ( 1917): LeafView.dispatchTouchEvent(),ACTION_DOWND/eve     ( 1917): LeafView.onTouchEvent(),ACTION_DOWND/eve     ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 1917): LeafView.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): LeafView.onTouchEvent(),ACTION_MOVED/eve     ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 1917): LeafView.dispatchTouchEvent(),ACTION_MOVED/eve     ( 1917): LeafView.onTouchEvent(),ACTION_MOVED/eve     ( 1917): TestViewEvent.dispatchTouchEvent(),ACTION_UPD/eve     ( 1917): OuterLayout.dispatchTouchEvent(),ACTION_UPD/eve     ( 1917): OuterLayout.onInterceptTouchEvent(),ACTION_UPD/eve     ( 1917): InnerLayout.dispatchTouchEvent(),ACTION_UPD/eve     ( 1917): InnerLayout.onInterceptTouchEvent(),ACTION_UPD/eve     ( 1917): LeafView.dispatchTouchEvent(),ACTION_UPD/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_DOWND/eve     ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_DOWND/eve     ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_DOWND/eve     ( 3673): InnerLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     ( 3673): LeafView.dispatchTouchEvent(),ACTION_DOWND/eve     ( 3673): LeafView.onTouchEvent(),ACTION_DOWND/eve     ( 3673): InnerLayout.onTouchEvent(),ACTION_DOWND/eve     ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): InnerLayout.onTouchEvent(),ACTION_MOVED/eve     ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_MOVED/eve     ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_MOVED/eve     ( 3673): InnerLayout.onTouchEvent(),ACTION_MOVED/eve     ( 3673): TestViewEvent.dispatchTouchEvent(),ACTION_UPD/eve     ( 3673): OuterLayout.dispatchTouchEvent(),ACTION_UPD/eve     ( 3673): OuterLayout.onInterceptTouchEvent(),ACTION_UPD/eve     ( 3673): InnerLayout.dispatchTouchEvent(),ACTION_UPD/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_DOWND/eve     ( 5388): OuterLayout.dispatchTouchEvent(),ACTION_DOWND/eve     ( 5388): OuterLayout.onInterceptTouchEvent(),ACTION_DOWND/eve     ( 5388): OuterLayout.onTouchEvent(),ACTION_DOWND/eve     ( 5388): TestViewEvent.onTouchEvent(),ACTION_DOWND/eve     ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.onTouchEvent(),ACTION_MOVED/eve     ( 5388): TestViewEvent.dispatchTouchEvent(),ACTION_UPD/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事件都会只走到这一层后,就开始停止继续向下走“隧道“,而是从当前层开始“冒泡“。

2 0
原创粉丝点击