Android触摸事件的响应机制

作者: Touchscreen     时间:2015-06-17     源于:科技快报    总点击:
【导读】:在用户的一次单手指触摸屏幕过程中,简单的讲,会按顺序产生一个ACTION_DOWN,若干个ACTION_MOVE,和一个ACTION_UP,我们下面的讨论也会基于这个简单case。

    北京时间06月17日消息,中国触摸屏网讯,用户触摸屏幕所产生的Touch Event在Android里是用一个MotionEvent对象来传递和处理的,我们最关注的是MotionEvent里的action,可以看到有ACTION_DOWN, ACTION_UP,ACTION_MOVE,ACTION_CANCEL,ACTION_POINTER_DOWN,ACTION_POINTER_UP等等很多种,在这里面最需要关注的是ACTION_DOWN和ACTION_UP,它们一个代表了用户按压动作的开始,一个代表了用户按压动作的结束,其他的一些ACTION基本都是发生在这2个ACTION之间的(ACTION_CANCEL等特殊的暂不讨论)。

    本文来自:http://www.51touch.com/touchscreen/news/front/201506/17-36507.html

在用户的一次单手指触摸屏幕过程中,简单的讲,会按顺序产生一个ACTION_DOWN,若干个ACTION_MOVE,和一个ACTION_UP,我们下面的讨论也会基于这个简单case。

触摸事件的传递处理顺序

一个触摸事件首先是在硬件层面触发,然后逐层传递到软件直至我们的app,前面的细节一般来说不用了解,我们讨论的事件入口从Activity开始。

笼统的说(实际上细节有所不同,下面会提到)

触摸事件的传递顺序是:

Activity -> 当前活动窗口(PhoneWindow) -> 窗口的Top-Level View(DecorView) -> 各级ViewGroup (如各种Layout) -> ... -> 各级ViewGroup -> 叶子节点 View

而触摸事件的处理顺序则刚好相反:

叶子节点 View -> 各级ViewGroup -> ... -> 各级ViewGroup -> (Window和DecorView只有分发没有处理) -> Activity

当叶子节点View接受到事件之后,会试图做出处理,如果它处理了,则上面各层不再处理,如果它没有处理则往上由它的父ViewGroup处理,这样逐层向上按顺序试图处理,直到Activity。

分发和处理的关键函数

从上面笼统提到的事件分发处理顺序可以看到,关键的分发处理集中在Activity,ViewGroup和View中,那么对于它们来说,有如下几个分发和处理的关键函数,这里先做一个简单介绍,后续再做详细说明。

1,boolean dispatchTouchEvent(MotionEvent event)

这个函数是最关键的分发处理函数,里面既包含了分发的逻辑,又包含了对处理的逻辑调用

分发逻辑:这个函数会先调用子view的dispatchTouchEvent进行分发

处理逻辑:如果子view没有对事件进行消化处理的话,这个函数就会调用本UI组件的处理函数如onTouchEvent

函数返回值表示这个触摸事件是否被当前这个UI组件(Activity/ViewGroup/View)消化处理了

2,boolean onTouchEvent(MotionEvent event)

这个函数是UI组件自己实现用来响应处理触摸事件的

函数返回值表示这个触摸事件是否被当前这个UI组件(Activity/ViewGroup/View)消化处理了

3,View.OnTouchListener: boolean onTouch(View v, MotionEvent event)

这个函数不是Activity/ViewGroup/View本身的响应处理函数,而是一个Listener的响应处理函数

需要给View通过setOnTouchListener来设置Listener以使得这个onTouch函数能够起作用

Activity没有setOnTouchListener

函数返回值表示这个触摸事件是否被当前这个UI组件(Activity/ViewGroup/View)消化处理了

4,ViewGroup: boolean onInterceptTouchEvent(MotionEvent event)

这个函数表明是否要拦截这个事件,前面提到过事件的分发顺序是在View tress里从根到叶子逐层分发,处理则是反向的从叶子到根逐层处理,onInterceptTouchEvent如果返回true,则表示我这一层要拦截这个事件自己进行处理了,不要把它分发到子View里

这个函数只有ViewGroup含有,View没有,因为View已经是叶子节点了,没有子View了

这个函数默认是返回false的,一般不要轻易override它,因为常规来讲是应该由子View先响应处理的

分发和处理的细节流程

ACTION_DOWN的处理

前面说过ACTION_DOWN是一个触摸动作的起始,所以对ACTION_DOWN的处理和对其他事件的处理在细节上是有不同的,各个UI组件对于ACTION_DOWN事件的处理流程可以看到如下:

(注意这里 view 表示一个视图组件,它可以是一个 View 也可以是一个 ViewGroup)

Activity -> dispatchTouchEvent:

通过getWindow().superDispatchTouchEvent(event)把事件分发到当前活动窗口(PhoneWindow),之后是 窗口的Top-Level View(DecorView),调用了DecorView的dispatchTouchEvent,DecorView继承自ViewGroup,所以这里实际上就进入了ViewGroup层面的dispatchTouchEvent

如果superDispatchTouchEvent最终返回true(即下层的某个ViewGroup或者View消化处理了该函数,dispatchTouchEvent返回true),则直接返回

如果返回值为false,则调用Activity的onTouchEvent对事件进行处理

ViewGroup -> dispatchTouchEvent:

首先是检查本view里是否保存了一个motion target(步骤4提到了怎么设置motion target),如果有则清除它

然后是调用onInterceptTouchEvent看这个事件是否需要被自己拦截,如果返回true,则直接进入步骤7开始自己处理事件的流程

如果返回false,则需要遍历所有的子view,遍历的顺序是:

首先按照Z-order

在同一Z值下如果可以的话按照子view的drawing order,这里的drawing order需要ViewGroup的子类override了getChildDrawingOrder才会实际生效

遍历子view的时候,如果这个触摸事件发生的位置在这个view的视觉范围以内,那么就调用child.dispatchTouchEvent将事件分发给这个子view,如果这个子view消化处理了这个事件(即dispatchTouchEvent返回true),本view会将该子view赋值给一个表明motion target的变量,且此时跳出循环

循环遍历结束后,如果有子view处理了该事件(即motion target不为空),则返回true表明此事件已经被处理

如果没有任何一个子view处理了该事件(即motion target不为空),则本view需要进行处理,进入步骤6

查看当前view是否注册了OnTouchListener,如果有,则调用该listener的onTouch函数来处理事件,如果onTouch返回true表示消化处理了该事件,则直接返回true

如果onTouch返回false表示没有处理,则继续调用本view的onTouchEvent函数来处理事件,这里会返回onTouchEvent的返回值

View -> dispatchTouchEvent:

查看当前view是否注册了OnTouchListener,如果有,则调用该listener的onTouch函数来处理事件,如果onTouch返回true表示消化处理了该事件,则直接返回true

如果onTouch返回false表示没有处理,则继续调用本view的onTouchEvent函数来处理事件,这里会返回onTouchEvent的返回值

其他ACTION的处理

上面说了ACTION_DOWN的处理,那ACTION_DOWN后续的ACTION_MOVE,ACTION_UP之类的事件又是怎么处理的呢?它们的处理方式略有不同:

在Activity层面来看,它们的处理和ACTION_DOWN没有区别。

在ViewGroup层面来看,处理开始有了差异:

ViewGroup -> dispatchTouchEvent:

检查当前view在之前处理ACTION_DOWN(ACTION_MOVE,ACTION_UP之类的事件一定是有一个配对的ACTION_DOWN事件在前面发生)的时候是否已经设置了一个motion target

如果有这个target,那么表明之前的ACTION_DOWN事件就是由该子view处理的,此时直接调用motion_target.dispatchTouchEvent

如果没有这个target,那么表明之前的ACTION_DOWN没有任何一个子view处理,那么后续这些事件也不要发给子view了,直接自己处理,进入步骤4

查看当前view是否注册了OnTouchListener,如果有,则调用该listener的onTouch函数来处理事件,如果onTouch返回true表示消化处理了该事件,则直接返回true

如果onTouch返回false表示没有处理,则继续调用本view的onTouchEvent函数来处理事件,这里会返回onTouchEvent的返回值

注意整个流程中都跳过了onInterceptTouchEvent的拦截

在View层面来看,处理方式也是一样的。

所以这里可以看到的现象就是:ACTION_DOWN被谁处理了,后续的ACTION_MOVE,ACTION_UP等事件最终也会交由谁处理。

onTouchEvent() or OnTouchListener.onTouch()?

从上面的内容可以看出来,我们要想对一个触摸事件进行响应,可以在view的onTouchEvent()函数里处理,也可以给view设置一个OnTouchListener然后在listener的onTouch()函数里处理,那么它们有些什么区别?我们应该怎么选择呢?

首先看区别:

onTouchEvent()是View自己的函数,对于我们来说无法override 各种View或者ViewGroup的onTouchEvent()函数,只能是在自己自定义的view里面实现override;而OnTouchListener的onTouch()是可以对任何View/ViewGroup起作用的,我们只需要在代码里为该View/ViewGroup加一个listener就行

OnTouchListener的onTouch()的执行顺序在view的onTouchEvent()之前,如果在onTouch()函数里面响应完了触摸事件并返回true之后,onTouchEvent()是不会被调用的

知道了区别,那么我们就可以轻易的做出选择了(只是一家之言,欢迎各种不同意见):

OnTouchListener的onTouch()基本上是万能的,任何时候都可以用它,所以大部分时候直接用它就行了

在自定义view里面,如果出于代码结构和功能清晰的目的,可以使用onTouchEvent()

 


    触摸屏与OLED网推出微信公共平台,每日一条微信新闻,涵盖触摸屏材料、触摸屏设备、触控面板行业主要资讯,第一时间了解触摸屏行业发展动态。关注办法:微信公众号“i51touch” 或微信中扫描下面二维码关注,或这里查看详细步骤
相关阅读:3D打印    模具产业    微铸锻技术    石墨烯    OLED面板    夏普    iPhone 8    半导体    苹果    
关于我们 | 广告服务 | 联系我们 | 版权声明 | 隐私政策 | 网站地图 | 友情链接 | 欢迎投稿 | 加入收藏 | 意见反馈 | 经销商加入
网站广告、经销商加盟、触摸屏软件销售: 028-85108892 13183843395 028-66219290 联系人: 张小姐 产品购买联系方式如下:
地址:成都市高升桥东路2号高盛中心1109室 电话: 028-85108892 13183843395 028-66219290
版权所有 Copyright(C) 2003-2015 All rights reserved 中国触摸屏网 电子邮件: 51touch@126.com touch8@gmail.com
业务合作QQ:触摸屏技术,触摸屏报价,触摸屏软件咨询 43361182 触摸屏软件制作与技术支持:触摸屏软件,触摸查询系统,触摸查询软件 893008608 媒体合作QQ: 893008608

2000人超级QQ触摸屏群:59897879 171220106


分享到