Android事件分发机制流程详解(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/woshizisezise/article/details/79290562

前言:今天来捋一捋Android的事件分发机制吧,其实很多人都知道有这么个东西,但是具体的事件执行流程没有认真的研究过分析过的,还是很难捋清楚其中的关系的,毕竟从activity到ViewGroup到View,有那么多的dispatchTouchEvent、onTouchEvent,ViewGroup中还多了个onInterceptTouchEvent方法,方法都可以返回true、false或者super.方法,所以涉及到的情况就比较多了,不研究清楚的话很容易说不上来事件会怎么执行,但是一旦你弄清楚了,以后保证你绝对忘不了了,虽然网上有很多的教程,但是那是别人的,与其让我去理解吸收他们的思想和逻辑,不如自己好好整理一下写下来,以后这也是自己的一个复习资料,所以各位看官,不喜勿喷哈~


首先我们要明白一件事,那就是Android中事件分发顺序:Activity(Window) -> ViewGroup -> View,贴出下图:

这里写图片描述

其中:

super:调用父类方法
true:消费事件,即事件不继续往下传递
false:不消费事件,事件继续往下传递 / 交由给父控件onTouchEvent()处理

那么这三个方法都是在什么时候会被调用呢?看看下面这张图:
这里写图片描述

下面我们将一步步的验证这个逻辑。


一、写程序、分析流程、验证结果

1.1 新建工程初始化代码

1.1.1 在MainActivity中添加初始代码

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        view = findViewById(R.id.myview);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.out.println("触发了点击事件");
            }
        });
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "dispatchTouchEvent ACTION_UP");
                break;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onTouchEvent ACTION_UP");
                break;
        }
        return false;
    }
}

1.1.2新建一个MyRelativeLayout继承RelativeLayout,初始代码如下

public class MyRelativeLayout extends RelativeLayout {


    private static final String TAG = "MyRelativeLayout";

    public MyRelativeLayout(Context context) {
        super(context);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "dispatchTouchEvent ACTION_UP");
                break;
        }
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onInterceptTouchEvent ACTION_UP");
                break;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onTouchEvent ACTION_UP");
                break;
        }
        return false;
    }
}

1.1.3 新建一个MyView继承View,初始代码如下

public class MyView extends View {

    private static final String TAG = "MyView";

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "dispatchTouchEvent ACTION_UP");
                break;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent ACTION_DOWN");
                break;

            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onTouchEvent ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onTouchEvent ACTION_UP");
                break;
        }
        return false;
    }
}

1.1.4 activity_main.xml中引入上面的两个自定义控件

<?xml version="1.0" encoding="utf-8"?>
<com.zy.toucheventdemo.MyRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_touch_event"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.zy.toucheventdemo.MyView
        android:id="@+id/myview"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="#ff3839" />

</com.zy.toucheventdemo.MyRelativeLayout>

1.2 修改条件、分析流程、验证结果

在这个步骤里我们依次修改各方法return的返回值,然后在程序运行之前分析一下执行顺序,然后运行程序验证我们的分析。

测试1:

条件:

MainActivity:
dispatchTouchEvent:return false;
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return false;
onInterceptTouchEvent:return false;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

所有类中的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent方法都return false,结果是什么?

分析:根据文首的图片分析可知,都返回false的时候,事件在进入到MainActivity的dispatchTouchEvent方法时,事件并不会继续往下传递,而是直接消费掉了,所以应该执行的是MainActivity的dispatchTouchEvent()方法中的ACTION_DOWN、ACTION_MOVE、ACTION_UP三个方法,执行代码看看输出结果:
这里写图片描述


测试2:

条件:

MainActivity:
dispatchTouchEvent:return true;
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return false;
onInterceptTouchEvent:return false;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return true;结果是什么?

分析:当修改MainActivity中的dispatchTouchEvent方法返回true时,事件被消费掉了,也不会往下传递,这个时候输出结果应该和测试1中的结果一样。
这里写图片描述

小结:

到这里我们可以看到,不管Activity中的dispatchTouchEvent return true还是false;事件都被消费了,不会往下传递,与流程图所示一致。


测试3:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return false;
onInterceptTouchEvent:return false;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);结果是什么?

分析:当MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);说明这个时候会调用父类方法,事件向ViewGroup中传递,这个时候事件就会传递到MyRelativeLayout的dispatchTouchEvent方法中,由于MyRelativeLayout的dispatchTouchEvent方法是return false,表示事件将交由给父控件onTouchEvent方法处理,即MainActivity的onTouchEvent()方法会响应事件,这个时候不论MainActivity的onTouchEvent()方法return true或者return false;事件都会被消费。
这里写图片描述


测试4:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return true;
onInterceptTouchEvent:return false;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);MyRelativeLayout中的dispatchTouchEvent方法return true;结果是什么?

分析:接上,MainActivity会向MyRelativeLayout传递事件,并且MyRelativeLayout中的dispatchTouchEvent方法return true;所以到这里,事件就会被消费掉,我们这里分析的流程应该如下:

MainActivity:dispatchTouchEvent ACTION_DOWN
MyRelativeLayout:dispatchTouchEvent ACTION_DOWN
MainActivity:dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: dispatchTouchEvent ACTION_MOVE
MainActivity:dispatchTouchEvent ACTION_UP
MyRelativeLayout: dispatchTouchEvent ACTION_UP

这里写图片描述


测试5:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return false;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

接上,这个时候将MyRelativeLayout中dispatchTouchEvent方法return super.dispatchTouchEvent(ev);返回结果是什么?

分析:在之前测试的基础之上,将MyRelativeLayout中dispatchTouchEvent方法return super.dispatchTouchEvent(ev);那么事件将会继续向下传递到onInterceptTouchEvent()方法中,这个方法主要是用来判断是否拦截该事件,我们一开始默认的是return false;所以并不会拦截,事件将会继续向下传递到MyView的dispatchTouchEvent()方法中,由于MyView的dispatchTouchEvent()方法默认也是return false;所以MyView不会处理该事件,事件交由父类MyRelativeLayout的onTouchEvent()方法执行,所以我们分析的执行流程应该是:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout:dispatchTouchEvent ACTION_DOWN
MyRelativeLayout:onInterceptTouchEvent ACTION_DOWN
MyView:dispatchTouchEvent ACTION_DOWN
MyRelativeLayout:onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
到这里要注意了,事件已经从MyView传递到MyRelativeLayout又传递到MainActivity中了,所以接下来的事件都是在MainActivity中进行
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP
这里写图片描述


测试6:

条件:
MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return true;
onTouchEvent:return false;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

接上,将MyRelativeLayout中的onInterceptTouchEvent()方法中修改为return true;结果是什么?

分析:其他步骤不变,当事件传到onInterceptTouchEvent()方法中,return true;表示拦截此事件,那么事件就会传递到onTouchEvent()方法中,由于onTouchEvent()方法return false;那么事件又会传递到MainActivity的onTouchEvent()方法中,所以我们的逻辑应该是这样的:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyRelativeLayout:onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP
这里写图片描述


测试7:

条件:
MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return true;
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

接上,现在将MyRelativeLayout中onTouchEvent()方法return true;结果是什么?

分析:前面步骤不变,当事件传递到MyRelativeLayout中onTouchEvent()方法时,这个时候return true;表示事件将会被消费,所以我们的逻辑应该是这样的:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyRelativeLayout: onTouchEvent ACTION_DOWN
注意:剩下的事件将从Activity->ViewGroup依次进行
MainActivity:dispatchTouchEvent ACTION_MOVE
MyRelativeLayout:dispatchTouchEvent ACTION_MOVE
这个时候就不会走onInterceptTouchEvent方法了
MyRelativeLayout:onTouchEvent ACTION_MOVE
MainActivity:dispatchTouchEvent ACTION_UP
MyRelativeLayout:dispatchTouchEvent ACTION_UP
MyRelativeLayout:onTouchEvent ACTION_UP
这里写图片描述


测试8:

条件:
MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return false;
onTouchEvent:return false;

接上,现在将MyRelativeLayout中的onInterceptTouchEvent()方法改为return super.onInterceptTouchEvent(ev);结果是什么?

分析:前面步骤不变,将MyRelativeLayout中的onInterceptTouchEvent()方法改为return super.onInterceptTouchEvent(ev);表示不拦截事件,事件将会继续往下传递到MyView的dispatchTouchEvent()方法中,由于MyView的dispatchTouchEvent()方法 return false;后续的流程和测试5一样,所以onInterceptTouchEvent()方法return super.onInterceptTouchEvent(ev);或者return false;效果一样,和首图表示一致。


测试9:

条件:
MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return true;
onTouchEvent:return false;

接上,现在将MyView中dispatchTouchEvent()方法修改为return true;结果是什么?

分析:从测试8中我们知道,由于MyView中dispatchTouchEvent()方法return false;导致事件向上传递,那么这么修改为return true;表示事件将会被消费掉,所以我们可以得出一下逻辑:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyView: dispatchTouchEvent ACTION_DOWN
这里return true;事件会被消费,不向下传递了
MainActivity: dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: dispatchTouchEvent ACTION_MOVE
注意,由于onInterceptTouchEvent return super.onInterceptTouchEvent(ev)/false;View的dispatchTouchEvent消费事件时,onInterceptTouchEvent方法的ACTION_MOVE会执行
MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE
MyView: dispatchTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MyRelativeLayout: dispatchTouchEvent ACTION_UP
MyRelativeLayout: onInterceptTouchEvent ACTION_UP
MyView: dispatchTouchEvent ACTION_UP
这里写图片描述


测试10:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return super.dispatchTouchEvent(event);
onTouchEvent:return false;

接上,现在将MyView中dispatchTouchEvent()方法修改为return super.dispatchTouchEvent(event);结果是什么?

分析:前面步骤相同,当MyView中dispatchTouchEvent()方法修改为return super.dispatchTouchEvent(event);可以知道事件将会往下传递到onTouchEvent方法中,默认是return false;所以事件又会被传递到MyRelativeLayout的onTouchEvent方法中去,这个时候MyRelativeLayout的onTouchEvent方法是return true;所以这里我们就分析这一种情况,return false的情况大家自己分析即可,所以我们得到的逻辑应该是这样的:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyView: dispatchTouchEvent ACTION_DOWN
MyView:onTouchEvent ACTION_DOWN
由于是return false;事件向上传递到MyRelativeLayout的onTouchEvent方法中
MyRelativeLayout:onTouchEvent ACTION_DOWN

MainActivity:dispatchTouchEvent ACTION_MOVE
MyRelativeLayout:dispatchTouchEvent ACTION_MOVE
由于MyView中的dispatchTouchEvent 不消费事件,所以onInterceptTouchEvent 的后续不会执行
MyRelativeLayout:onTouchEvent ACTION_MOVE
MainActivity:dispatchTouchEvent ACTION_UP
MyRelativeLayout:dispatchTouchEvent ACTION_UP
MyRelativeLayout:onTouchEvent ACTION_UP
这里写图片描述


测试11:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return super.dispatchTouchEvent(event);
onTouchEvent:return true;

接上,现在将MyView的onTouchEvent修改为return true;结果是什么?

分析:事件会一路传递到MyView中,并且有onTouchEvent接收并消费掉,所以我们分析一下逻辑应该是:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyView: dispatchTouchEvent ACTION_DOWN
MyView: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE
MyView:dispatchTouchEvent ACTION_MOVE
MyView:onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MyRelativeLayout: dispatchTouchEvent ACTION_UP
MyRelativeLayout: onInterceptTouchEvent ACTION_UP
MyView:dispatchTouchEvent ACTION_UP
MyView:onTouchEvent ACTION_UP
这里写图片描述

总结

这里我们对ViewGroup的onInterceptTouchEvent()方法做一个小结,结合上述几个测试的结果可以分析得到:

  1. 当onInterceptTouchEvent()方法return true;时表示拦截该事件交由onTouchEvent()方法执行,以后的onInterceptTouchEvent()方法的move,up方法都不会执行了。
  2. 如果事件交由ViewGroup的子View并且消费了的话,那么ViewGroup的onInterceptTouchEvent()方法还是会依次执行move,up方法。
  3. 如果事件传递到子View,但是子View不处理向上返回到ViewGroup的onTouchEvent()方法中,那么onInterceptTouchEvent()方法的move,up方法都不会执行。

由此可见,只有当事件从ViewGroup传递到子View并且消费了事件的话,ViewGroup的onInterceptTouchEvent()方法中的move,up方法才会执行。


如果大家上面的所有测试方法都能看懂并且在不看运行结果的情况下,根据条件所给的返回值,能够正确的分析出执行流程,那么恭喜你,你已经了解了事件分发的机制,我们晚点再讲源码部分,但是不知道大家有没有发现一个问题,为什么我在Activity中给view.setOnClickListener中的onClick回调方法并没有执行呢?事件好像就消失不见了,这里大家要注意下了,onClick方法没有执行是因为MyView中的onTouchEvent方法应该是return super.onTouchEvent(event);这样事件才能交由onClick方法执行,不信我们可以看看测试12的结果。

测试12:

条件:

MainActivity:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onTouchEvent:return false;

MyRelativeLayout:
dispatchTouchEvent:return super.dispatchTouchEvent(ev);
onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);
onTouchEvent:return true;

MyView:
dispatchTouchEvent:return super.dispatchTouchEvent(event);
onTouchEvent:return super.onTouchEvent(event);

分析:我们主要分析一下事件的执行流程,从MainActivity->MyRelativeLayout->MyView->onClick();我们来写一写流程:

MainActivity: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
MyView: dispatchTouchEvent ACTION_DOWN
MyView: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: dispatchTouchEvent ACTION_MOVE
MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE
MyView: dispatchTouchEvent ACTION_MOVE
MyView: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MyRelativeLayout: dispatchTouchEvent ACTION_UP
MyRelativeLayout: onInterceptTouchEvent ACTION_UP
MyView: dispatchTouchEvent ACTION_UP
MyView: onTouchEvent ACTION_UP
onClick:触发了点击事件
这里写图片描述

可以看到,事件正如我们所分析的一样往下执行,当所有的down,move,up方法执行完后就会执行最后的onClick方法。


其实当控件被点击时,在View的onTouchEvent()方法执行之前,还有另一个方法会优先执行,这个方法就是onTouch()方法,在View中无法重写这个方法,只能通过在给view.setOnTouchListener(new View.OnTouchListener() {});的形式,才能执行onTouch()方法,下面我们在MainActivity中给view添加一个onTouch事件监听器并打印日志

view.setOnTouchListener(new View.OnTouchListener() {
  @Override
    public boolean onTouch(View v, MotionEvent event) {
        System.out.println("执行了onTouch(), 动作是:" + event.getAction());
        return false;
    }
});

这里默认就是return false;

之前我们已经说过了,onTouch方法会优先于onTouchEvent方法执行,所以我们来分析一下这里返回的true或者false对于事件分发有什么影响?

当onTouch方法return true;时,事件执行到这儿的时候,应该就被消费掉了,onTouchEvent方法就不会继续执行,所以我们的onClick方法就不会被回调。

当onTouch方法return false;时,事件不会被消费,交由onTouchEvent方法执行,根据onTouchEvent方法的返回值决定onClick方法是否执行。

我们来试一试,看看两者的区别。

测试13:

  • 当onTouch return false;
    这里写图片描述

可以看到onTouch方法确实是在onTouchEvent方法之前执行,这里的动作0代表down,1代表up,2代表的是move。并且当onTouch返回false的时候,和我们分析的一样,事件不拦截,继续往下传递,最终回调了onClick方法。

  • 当onTouch return true;
    这里写图片描述

可以看到,当我们的事件执行到onTouch时,因为被消费了,所以并不会继续往下走,当执行完ACTION_UP后,事件就被消费完了,onClick并不会被回调。

注意: 也许会有人问,如果onTouch return的是super.onTouch(event)呢?那事件会怎么执行?哈哈哈,不好意思,并没有super.onTouch(event)这个方法,所以onTouch方法只能是return true;或者return false;


由于篇幅过长,这一篇咱们就先通过分析和测试捋清楚Android事件分发的过程和逻辑,如果大家能看懂,相信大家已经离真相不远了,具体的源码分析咱们就留到下一篇再来分析吧!

猜你喜欢

转载自blog.csdn.net/woshizisezise/article/details/79290562
今日推荐