完全理解android事件分发机制

前言

之前笔者其实已经写过事件分发机制的文章:快速理解android事件传递拦截机制概念
但是,现在看来其实更像是一篇知识概括,多处可能未讲清楚,于是打算重写事件分发,用一篇文章大致讲清楚。
首先,形式上笔者最先思考的是使用源码,此者能从原理上讲解分发机制,比起侃侃而谈好得多。但是大量的源码往往会让新手产生畏惧难以理解,于是笔者最终还是打算主要使用实例log输出来让读者理解android事件分发。

重要函数

笔者此次主要提及最常用的几个函数:
(其间区别看源码很容易理解,此处直接给上结果)
onClick():这个函数是是View提供给我们的OnClickListener这个接口中的函数,在这里可以自定义对点击事件的处理逻辑。会在onTouchEvent()中进行调用。
onTouch():这个函数是View提供给我们的OnTouchListener这个接口中的函数,在这里面可以自定义对触摸事件的处理逻辑。
onTouchEvent():这个函数是view内部的触摸事件的处理方式,其间包括获取焦点,调用onClick()等等。
dispatchTouchEvent():这个是View的事件分发函数,在ViewGroup中进行重写。在View中其间会调用onTouchEvent(),在ViewGroup中其间会调用onInterceptTouchEvent()和onTouchEvent()。
onInterceptTouchEvent():这个函数是事件拦截函数,是ViewGroup才有的函数。

重要函数执行顺序

此处我们通过一个很简单的例子进行说明,示例:
这里写图片描述
示例xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<com.example.double2.dispatchevent.LinearLayoutA
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_a"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_dark"
    android:padding="30dp"
    android:orientation="vertical"
    >

    <com.example.double2.dispatchevent.LinearLayoutB
        android:id="@+id/ll_b"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:padding="30dp"
        android:orientation="vertical"
        >

        <com.example.double2.dispatchevent.LinearLayoutC
            android:id="@+id/v_c"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            />
    </com.example.double2.dispatchevent.LinearLayoutB>
</com.example.double2.dispatchevent.LinearLayoutA>



由外到里主要是三个LinearLayout,分别为A、B、C。笔者分别在五个关键函数中加上了Log,最终点击一下,输出的值如下:

01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_C

如上,我们可以看到五个函数的大致执行顺序如下:

  1. dispatchTouchEvent()
  2. onInterceptTouchEvent()
  3. onTouch()
  4. onTouchEvent()
  5. onClick()

好奇的读者肯定会问,为什么事件分发执行了两次呢?
其实很简单,因为的确有两个分发的事件,一次是“手指按下”的事件,一次是“手指抬起”的事件。我们可以看到,只有在“手指抬起”的时候,才会触发onClick()事件。

此处为了便于大家理解,也附上一张事件分发的图:
这里写图片描述

控制事件分发

然而还有一个值得我们在意的事情,就是onTouch()以及onTouchEvent()只有在C中执行,而在B和A的就不执行了。
此处,我们就必须再讲一点了。

dispatchTouchEvent()返回值为true的时候,事件会继续向下分发,一旦返回值为false,事件就不再向下分发。
onInterceptTouchEvent()、onTouch()、onTouchEvent()这三个函数,返回值为false的时候,事件会继续向下分发,一旦返回值为true,事件就不再向下分发。
而onClick()没有返回值

根据这点我们可以知道,一定是C的onTouchEvent()中返回了true,我们将其更改后再看一下效果。
原来的代码为:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("XJJ","onTouchEvent_C");
        return super.onTouchEvent(event);
    }

更改后:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("XJJ","onTouchEvent_C");
        return false;
    }

效果:

01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_B

这样操作之后,我们可以发现,“手指按下”时,onTouch()以及onTouchEvent()事件就可以传递到B了。但是同时,我们也可以发现,当“手指抬起”时,C的onTouch()以及onTouchEvent()事件就不会执行了。

当然,如果需要onTouch()以及onTouchEvent()事件传递到A,那么只需要将B的onTouchEvent()也返回false即可。(此处就不重复演示了)

那么如果onTouchEvent()返回值设置为true了之后,是不是onClick()事件是不是就不会执行了呢?效果如下:

01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C

onClick()的确是不会执行了,如此我们也尝试一下onTouch()返回值设置为true,效果如下:

01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C

如此,我们可知,当onTouch()返回值设置为true的时候,onTouchEvent()的确是不会执行了。

到这里,我们其实只剩下对onInterceptTouchEvent() 的分析了,为何没有dispatchTouchEvent()了呢?
因为dispatchTouchEvent()是事件分发的函数,对View而言,我们阻止它内部的事件分发是没有什么意义的,而我们要控制ViewGroup的事件分发则是通过onInterceptTouchEvent() 来执行的。

如此我们便假设一个应用场景,A包括B,B包括C,B为横向滑动,C为竖向滑动,当横向滑动的加速度大于竖向滑动的加速度的时候,我们仅仅让B响应事件,而不把事件传递给C。

我们可以通过onInterceptTouchEvent() 来实现,仅仅只需将B的onInterceptTouchEvent()返回值设置为true即可,效果如下:

01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_B

源码

最后是附上demo的源码,大家可以自行下载理解。
http://download.csdn.net/detail/double2hao/9735367

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值