彻底了解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树中进行向下传递,传递的顺序依次是:
- 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_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事件都会只走到这一层后,就开始停止继续向下走“隧道“,而是从当前层开始“冒泡“。
- 彻底了解android view 事件机制中的“隧道”和“冒泡”
- WPF中的事件及冒泡事件和隧道事件(预览事件)的区别
- WPF中的事件及冒泡事件和隧道事件(预览事件)的区别
- 冒泡事件和预览事件(隧道事件)
- Android中的View事件传递机制
- [Android] 彻底了解Binder机制原理和底层实现
- [Android] 彻底了解Binder机制原理和底层实现
- Android View 和 ViewGroup 事件分发机制
- 了解事件冒泡和事件捕获
- 了解Gtest中的事件机制
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上,view)
- Android Touch事件在View层级结构中的传递机制
- 彻底弄懂JS的事件冒泡和事件捕获
- 彻底弄懂JS的事件冒泡和事件捕获
- 彻底弄懂JS的事件冒泡和事件捕获
- android View事件分发机制。
- Android View事件分发机制
- android view事件分发机制
- VS2013MFC对话框工程学习笔记五 - 了解窗口和窗口控件的属性并修改
- ubuntu14.04下安装matlab2015b
- hdu 5441 Travel(长春网络赛——并查集)
- Java学习笔记(一)
- 每个顶点指定流量的无源汇最小费用流(80人环游地球)
- 彻底了解android view 事件机制中的“隧道”和“冒泡”
- 数据结构之数组结构(一)
- VolleyAir
- 初步对消息队列RabbitMQ的了解
- 【UFLDL-exercise7-stacked autoencoder for digit classification】
- HibernateDaoSupport And SqlMapClientDaoSupport
- 初步认识继承
- OpenCV3.1.0+VS2013配置+Win7(64位)
- 欢迎使用CSDN-markdown编辑器