根据以下文章总结
原文出处:https://blog.youkuaiyun.com/github_26939093/article/details/51124443
文章推荐:https://blog.youkuaiyun.com/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事件都会只走到这一层后,就开始停止继续向下走“隧道“,而是从当前层开始“冒泡“。