东方锐智—让教育更美好

当前位置:锐智首页 > 技术博文-Android > 正文
完全理解android事件分发机制
时间:2017-06-17 11:56:51 来源:东方锐智 作者:东方锐智

重要函数

笔者此次主要提及最常用的几个函数: 
(其间区别看源码很容易理解,此处直接给上结果) 
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>1234567891011121314151617181920212223242526272829303112345678910111213141516171819202122232425262728293031

由外到里主要是三个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()、onInterceptTouchEvent() 、onTouch()、onTouchEvent()这四个函数,返回值为false的时候,事件会继续向下分发,一旦返回值为true,事件就不再向下分发。 
而onClick()没有返回值

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

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

更改后:

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

效果:

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_A01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C123456789101112131415123456789101112131415

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


学员感言
【13期嵌入式-江信程】
申请免费试听
姓名:
手机:
Q Q: