前言:智能手机早已成为我们今天身边必不可少的手持设备,iOS和Android也是目前主流的二大移动操作系统,当然也有越来越多的开发者加入到移动开发的工作中来。我也是一名普通的Android码农,目前也在学习iOS开发中,有一些成长道路上的一些经验和教训,很早就想动手记录下来,再三下决心,终鼓起勇气,敲起键盘,有了下文,初次行文,文笔未免羞涩,大神轻点喷~
我们都知道,触摸事件对我们移动App来说至关重要,人机交互从我们手指的各种触摸手势开始,手机屏幕检测到我们的触摸事件,手机操作系统会将这些触摸事件通过回调框架提供的定义好的接口,让我们App可以接收到这些触摸事件,从而开展我们自己的业务逻辑。在Android中,系统将触摸事件包装成MotionEvent对象,同时MotionEvent类内部定义了ACTION_DOWN, ACTION_UP, ACTION_MOVE,ACTION_CANCEL等常量来表示对应触摸事件,根据这些常量的名称,我们很好理解它们对应的含义。下面,我们先提出我们的结论,然后我们一步步去验证它们。
1.对于我们的App来说,触摸事件传递的起点从Activity的dispathTouchEvent()回调方法开始。
我们上代码验证,我们先写一个最简单的Activity,
很简单的布局文件
很简单的按钮点击
此时我们运行程序,点击按钮,控制台会打印 “btn clicked!” 日志,一切正常。
此时我们重写Activity的dispatchTouchEvent方法,
再次运行程序,点击按钮,没有打印日志,按钮也没有任何触摸响应。说明触摸事件此时被屏蔽了,为了弄清楚到底怎么回事,就要去看看Activity的 dispatchTouchEvent方法的源码。
这里分三步,第一步,如果是触摸开始类型ACTION_DOWN事件,会回调onUserInteraction()方法,所以在项目中可以考虑将一些需要用户开始触摸时就执行的代码放到此方法中;第二步,将触摸事件ev传递给Activity窗口绑定的根布局rootView,如果rootView也有子布局,会一级一级传递,具体过程我们下面会具体说明。所以如果所有布局的都没有消费此触摸事件ev,就会执行第三步 ,调用Activity的onTouchEvent()方法,否则此触摸事件ev在App层传递结束。
上面我们讲到Activity会将触摸事件ev传递给根布局rootView,rootView有二种类型,View和ViewGroup。
2.先说View,View触摸事件的开始还是dispatchTouchEvent(), 然后View会先将ev传递给TouchListener消费,如果TouchListener没有消费,就将ev传递给onTouchEvent,如果onTouchEvent没有消费,就会向上传递给它的父容器或者Activity,
还是上代码,我们先验证dispatchTouchEvent,我们先自定义一个button,
此时,我们运行程序,按下按钮,不要松开,滑动鼠标(我用的模拟器),真机此处换成手- ,-,然后松开按钮,查看控制台打印日志:
也非常好理解,我们查看MotionEvent源代码,ACTION_DONW = 0, ACTION_UP=1, ACTION_MOVE=2, 打印的日志与我们手指的触摸操作完全一致,先是触发了ACTION_DOWN事件,然后是几个ACTION_MOVE事件,最后是ACTION_UP事件。
接下来,我们给EventBtn添加onTouchListener,并返回true
运行App,查看打印的日志,
我们看到,没有打印 btn clicked日志,验证了我们说明的View事件传递的第一部分,触摸事件ev,优先被onTouchListener消费掉了,后面的onTouchEvent和onClickListener都不会再收到点击的触摸事件了。下面我们将onTouchListener的返回值改为false,并重写EventBtn onTouchEvent方法,并返回true。
运行App,点击按钮,查看打印的日志,
这也验证了我们关于View触摸事件传递的第二部分,触摸事件ev优先被onTouchListener消费,然后再让传递给onTouchEvent消费。
现在我们通过代码验证我们关于View触摸事件传递机制的结论,为了进一步佐证我们的结论,我们还得去查看View的框架源码,我们打开View的源码,找到dispatchTouchEvent方法,
看到源码,很一目了然,框架先调用onTouchListener,如果事件没有被消费掉,再调用onTouchEvent,最后result表示此触摸事件是否被消费,作为返回值。
下一篇日记我们去学习关于ViewGroup的 消息传递,具体请听下回分解~~