![Android App开发进阶与项目实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/109/44510109/b_44510109.jpg)
2.2.1 手势事件的分发流程
智能手机的一大革命性技术是把屏幕变为可触摸设备,既可用于信息输出(显示界面),又可用于信息输入(检测用户的触摸行为)。为方便开发者使用,Android已可自动识别特定的几种触摸手势,包括按钮的点击事件、长按事件、滚动视图的上下滚动事件、翻页视图的左右翻页事件等。不过对于App的高级开发来说,系统自带的几个固定手势显然无法满足丰富多变的业务需求。这就要求开发者深入了解触摸行为的流程与方法,并在合适的场合接管触摸行为,进行符合需求的事件处理。
与手势事件有关的方法主要有3个(按执行顺序排列),分别说明如下:
- dispatchTouchEvent:进行事件分发处理,返回结果表示该事件是否需要分发。默认返回true表示分发给子视图,由子视图处理该手势,不过最终是否分发成功还得根据onInterceptTouchEvent方法的拦截判断结果;返回false表示不分发,此时必须实现自身的onTouchEvent方法,否则该手势将不会得到处理。
- onInterceptTouchEvent:进行事件拦截处理,返回结果表示当前容器是否需要拦截该事件。返回true表示予以拦截,该手势不会分发给子视图,此时必须实现自身的onTouchEvent方法,否则该手势将不会得到处理;默认返回false表示不拦截,该手势会分发给子视图进行后续处理。
- onTouchEvent:进行事件触摸处理,返回结果表示该事件是否处理完毕。返回true表示处理完毕,无须处理上一级视图的onTouchEvent方法,一路返回结束流程;返回false表示该手势事件尚未完成,返回继续处理上一级视图的onTouchEvent方法,然后根据上一级onTouchEvent方法的返回值判断直接结束或由上上一级处理。
上述手势方法的执行者有3个(按执行顺序排列),具体说明如下:
- 页面类:包括Activity及其派生类。页面类可调用dispatchTouchEvent和onTouchEvent两个方法。
- 容器类:包括从ViewGroup类派生出的各类容器,如各种布局Layout和ListView、GridView、Spinner、ViewPager、RecyclerView、Toolbar等。容器类可调用dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个方法。
- 控件类:包括从View类派生的各类控件,如TextView、ImageView、Button等。控件类可调用dispatchTouchEvent和onTouchEvent两个方法。
只有容器类才能调用onInterceptTouchEvent方法,这是因为该方法用于拦截发往下层视图的事件,而控件类已经位于底层,只能被拦截,不能拦截别人。页面类没有下层视图,所以不能调用onInterceptTouchEvent方法。三类执行者的手势处理流程如图2-4所示。
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P50_3712.jpg?sign=1738963597-n7kqwBJ2xTD8SrHfmTv8uMU7paGgtz7A-0-d43da5d97a06404e1179dc0c84f3b287)
图2-4 三类执行者的手势处理流程
以上流程图涉及3个手势方法和3种手势执行者,尤其是手势流程的排列组合千变万化,并不容易解释清楚。对于实际开发来说,真正需要处理的组合并不多,所以只要把常见的几种组合搞清楚就能应付大部分开发工作,这几种组合说明如下。
(1)页面类的手势处理。它的dispatchTouchEvent方法必须返回super.dispatchTouchEvent,如果不分发,页面上的视图就无法处理手势。至于页面类的onTouchEvent方法,基本没有什么作用,因为手势动作要由具体视图处理,页面直接处理手势没有什么意义。所以,页面类的手势处理可以不用关心,直接略过。
(2)控件类的手势处理。它的dispatchTouchEvent方法没有任何作用,因为控件下面没有子视图,无所谓分不分发。至于控件类的onTouchEvent方法,如果要进行手势处理,就需要自定义一个控件,重写自定义类中的onTouchEvent方法;如果不想自定义控件,就直接调用控件对象的setOnTouchListener方法,注册一个触摸监听器OnTouchListener,并实现该监听器的onTouch方法。所以,控件类的手势处理只需关心onTouchEvent方法。
(3)容器类的手势处理。这才是真正要深入了解的地方。容器类的dispatchTouchEvent与onInterceptTouchEvent方法都能决定是否将手势交给子视图处理。为了避免手势响应冲突,一般要重写dispatchTouchEvent或者onInterceptTouchEvent方法。两个方法的区别可以这么理解:前者是大领导,只管派发任务,不会自己做事情;后者是小领导,尽管有拦截的权利,不过也得自己做点事情,比如处理纠纷等。容器类的onTouchEvent方法近乎摆设,因为需要拦截的在前面已经拦截了,需要处理的在子视图已经处理了。
经过上面的详细分析,常见的手势处理方法有下面3种:
- 页面类的dispatchTouchEvent方法:控制事件的分发,决定把手势交给谁处理。
- 容器类的onInterceptTouchEvent方法:控制事件的拦截,决定是否要把手势交给子视图处理。
- 控件类的onTouchEvent方法:进行手势事件的具体处理。
为方便理解dispatchTouchEvent方法,先看下面不派发事件的自定义布局代码:
(完整代码见event\src\main\java\com\example\event\widget\NotDispatchLayout.java)
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P51_17277.jpg?sign=1738963597-DsgLllVpfScluMA42fnQdRiSMqVJQbOA-0-672059006a5f7ab3de66b9ad88c3a809)
活动页面实现的onNotDispatch方法代码如下:
(完整代码见event\src\main\java\com\example\event\EventDispatchActivity.java)
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P51_3720.jpg?sign=1738963597-kuCSLh09GDWVCWITCtDKrypqYFDLEVdJ-0-964645ad66ef301c808a9e37c6a837ab)
不派发事件的处理效果如图2-5和图2-6所示。图2-5的上面部分为正常布局,此时按钮可正常响应点击事件;图2-6的下面部分为不派发布局,此时按钮不会响应点击事件,取而代之的是执行不派发布局的onNotDispatch方法。
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P52_6593.jpg?sign=1738963597-ZZuCIm2tYz2t0NZ5SbizM9bM9cfsfqIV-0-fda455fe6757ba8cb34be7ef053acc45)
图2-5 正常布局允许分发事件
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P52_6594.jpg?sign=1738963597-dDIgosLrUZi3RBMANAn16I8Tsi1tPbdc-0-2e5f273cbfe11dd195f1a70e57e16393)
图2-6 不派发布局未分发事件
为方便理解onInterceptTouchEvent方法,再看拦截事件的自定义布局代码:
(完整代码见event\src\main\java\com\example\event\widget\InterceptLayout.java)
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P52_17280.jpg?sign=1738963597-uXxL65CHOptBcCsSuCjeQAqbOVp2Pxkt-0-47b9853d25a90c452791d2c2f8787c98)
活动页面实现的onIntercept方法代码如下:
(完整代码见event\src\main\java\com\example\event\EventInterceptActivity.java)
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P53_17281.jpg?sign=1738963597-diMLhoqiw01mjE6D8sSstaYzb2rVz4Fs-0-f35e4e13769d5c5e50146e7b8d90e218)
拦截事件的处理效果如图2-7和图2-8所示。图2-7的上面部分为正常布局,此时按钮可正常响应点击事件;图2-8的下面部分为拦截布局,此时按钮不会响应点击事件,取而代之的是执行拦截布局的onIntercept方法。
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P53_6617.jpg?sign=1738963597-ypepBtOLhbqMt6fHpQJJRvOXYpn4h5ZD-0-023880ae3dd7a3fe79b46c160a77160f)
图2-7 正常布局不拦截事件
![](https://epubservercos.yuewen.com/0C084D/23721623101012206/epubprivate/OEBPS/Images/Figure-P53_6618.jpg?sign=1738963597-TSV0fu3BLC8N72EnLnQqJoIDAg56eN9v-0-d9bfe56cd2c1f4ad2afc91dc2e827066)
图2-8 拦截布局会拦截事件