Android 事件分发拦截(基础篇)

来源:互联网 发布:淘宝抠图兼职怎么找 编辑:程序博客网 时间:2024/05/18 18:42

前言


android的事件分发,浅一点大家都懂,深一点我自己就hold不住了。写这篇文章是希望能帮助刚接触android的朋友对android事件分发有个简单的了解,也是对自己学习的一个归纳总结吧。(以下内容为未看源码的情况下写出,内容如果有误,请及时指出,我及时修正,以免误导他人,谢谢大家)


三个类

Activity

ViewGroup

View

三个方法

boolean dispatchTouchEvent(MotionEvent event)

boolean onInterceptTouchEvent(MotionEvent ev)

boolean onTouchEvent(MotionEvent event)


先说三个类

系统捕获到用户的触摸事件后,会进行一系列逻辑处理(具体需看源码),然后把这个触摸事件交给Activity,Activity会把这个触摸事件交给与之绑定的layout的最外层ViewGroup,如果ViewGroup中有子视图(可能是ViewGroup,也可能是View),看用户触摸到的是哪个子视图,然后将这个触摸事件继续传递,直到没有子视图为止(一般子视图是View的时候就不会再传递了)(这里有个前提,ViewGroup重新onInterceptTouchEvent(MotionEvent ev)的返回值不能为rue,稍后会说明原因)


再来说下三个方法

首先是 dispatchTouchEvent(MotionEvent ev) 方法,从方法名称可知这个方法是用来处理事件分发逻辑的,系统捕获到用户的触摸事件后,最终会调用Activity的 dispatchTouchEvent(MotionEvent ev)  方法来传递触摸事件,参数 MotionEvent  就是对触摸事件的封装,可以获取到触摸事件的类型(ACTION_DOWN:用户手按下屏幕、ACTION_MOVE:用户触摸屏幕并在屏幕上滑动、ACTION_DOWN:用户手抬起离开屏幕),用户的触摸区域的坐标等等(详情请自行查阅相关内容)

其次是 onInterceptTouchEvent(MotionEvent ev) 方法,从方法名称可知这个方法是用来拦截事件的,返回值为boolean类型,true代表本类要拦截此事件,此事件不再往子类传递;false代表不拦截,此事件继续往子类传递。Activity和View没有此方法。Activity是第一个接收到触摸事件的非系统类(因为是自己继承的Activity),它是不能进行事件拦截的,必须要要把事件传递给真正意义上的视图。而View因为没有子类,事件到了这里本身就不会再进行传递,所有没有拦截的必要。

最后是 onTouchEvent(MotionEvent event)方法,这里是真正意义上处理触摸事件的地方。Activity、ViewGroup、View都可以重新此方法,执行各自的触摸事件对应的逻辑。

介绍完了三个类三个方法后,开始上代码案例了;


界面、代码及结果展示


重写了MainActivity的dispatchTouchEvent()、onTouchEvent()方法



自定义TestTouchEventViewGroupParent的onTouchEvent()、dispatchTouchEvent()、onInterceptTouchEvent()三个函数



以及TestTouchEventView的onTouchEvent()、dispatchTouchEvent()方法



然后用手触摸蓝底的TestTouchEventView,看下log



简单解释一下log,首先Activity的dispatchTouchEvent()被系统调用,Activity找到他的子布局也就是TestTouchEventViewGroup,调用他的dispatchTouchEvent()方法,TestTouchEventViewGroup在dispatchTouchEvent()方法内部会调用onInterceptTouchEvent()来检测是否设置过事件拦截,系统默认为false(不拦截),检测完之后会将事件继续传递给子视图即TestTouchEventView并调用它的dispatchTouchEvent()方法,因为TestTouchEventView是最小的视图单元,事件不用再进行传递,在dispatchTouchEvent()方法中调用onTouchEvent,开始进行事件处理。onTouchEvent()有个boolean的返回值,true代表消耗了此次事件,它的父布局及父布局以上的ouTouchEvent()都不会再被执行,返回false代表此次事件未消耗,此次事件可以交由当前布局的副布局进行事件处理。(super.onTouchEvent(event) 默认返回false,切记)

当我们想处理一个控件的滑动事件时,我们需要重写onTouchEvent()方法,并在里面对事件类型做对应的处理,一轮事件默认的顺序是ACTION_DOWN、ACTION_MOVE.....ACTION_MOVE、ACTION_UP,当用户触摸屏幕产生事件(首先是ACTION_DOWN),系统捕获到会调用Activity的dispatchTouchEvent方法传递给Activity,Activity发现是ACTION_DOWN默认会继续往子类传递,传至TestTouchEventViewGroup,TestTouchEventViewGroup不进行拦截,传至TestTouchEventView。上图log中,TestTouchEventView没有消耗此次事件,事件处理权回到了TestTouchEventViewGroup手上,然后调用自己onTouchEvent()方法进行事件处理,如果还是没有消耗掉此次事件,事件的处理权最终回到了Activity手上,Activity在处理自己的onTouchEvent()方法,如果Activity在onTouchEvent()的事件处理中,也没有消耗掉此次事件(ACTION_DOWN),请注意!!!当用户产生第二个行为过来时,比如ACTION_MOVE,系统还是会捕获事件调用Activity的dispatchTouchEvent()方法进行事件分发,但这里会去检测之前ACTION_DOWN事件是否被消耗掉,在哪个视图内被消耗掉的,下一次的事件派发到那个视图就打止了,不会再往子视图传递事件。如果事件直到回到Activity时都还没被消耗掉,次轮事件不会再进行派发,每次到Activity就为止了。这也就是有的朋友去监听触摸事件,只能监听到ACTION_DOWN,后面的事件再也监听不到的原因了,因为除ACTION_DOWN的事件早在之前就被拦截了,根本没传下来。


总结

看完我上面内容,希望大家自己动手瞧一瞧,可以加深印象更好的理解,我这边还准备了一份java程序来模拟事件分发的内部简化执行逻辑,大家可以通过debug配合打印的log加深对事件分发机制的了解,下面是源码的git地址

GIT:https://github.com/MintLy/Android-TouchEventDemo.git

如果有说错或说的不对的地方请及时与我联系(mint_ly@163.com),我定会及时修正,以免误导他人,罪过!!!

后话

这是我第一次写博客,花了一晚上的时间,感觉写博客真心不容易,首先保证内容质量的同时还要间距排版问题。先向那些无私奉献的博客主门鞠一躬,没有你们的无私奉献我们不会走的这么容易。写博客及可以帮助别人也可以巩固自己对知识的理解,因为是要给别人看的,自己会更多的去思考(对于小白的我真心累阿...),希望这是一个美好的开始,大家一起学习进步!!! Fighting

原创粉丝点击