码迷,mamicode.com
首页 > 移动开发 > 详细

Android事件分发解析

时间:2016-07-17 17:11:10      阅读:361      评论:0      收藏:0      [点我收藏+]

标签:

事件分发机制
一.点击事件传播路径
1.点击事件TouchEvent最先是到达Activity的,然后传给Activity对应的window,再传给DecorView,再传给id为content的ViewGroup,即我们通过setContentView设置的ViewGroup,以此到最后的view。我们编程所能控制的由Activity,ViewGroup和View。
技术分享 
2.要把点击事件传给一个View或ViewGroup,要调用其dispatchTouchEvent(),要判断该View或ViewGroup是否拦截,要调用其onInterceptTouchEvent(),要判断该View或ViewGroup是否消耗,要调用其onTouchEvent()。子view没有onInterceptTouchEvent(),所以传给在子view的点击事件都是直接判断是否要消耗。当一个事件被消耗了就会消失掉。
 
技术分享 
3.自定义的ViewGroup和View默认都是不拦截点击事件的,ViewGroup决定拦截与否是通过onInterceptTouchEvent()的返回值决定的;ViewGroup决定消耗事件与否是通过onTouchEvent()决定的。View因为没有onInterceptTouchEvent(),所有决定拦截与否跟消耗与否是通过onTouchEvent()的返回值决定的,true表示拦截,false表示不拦截。注意分清拦截和消耗。
ViewGroup的onInterceptTouchEvent()如下
 

技术分享
4.在子View的dispatchTouchEvent()内部有这样一段代码
技术分享 
其逻辑是,如果View设置了onTouchListener,则其onTouchListener的onTouch()方法会被调用。如果该方法返回false,则会调用onTouchEvent(),但是如果该方法返回true,则onTouchEvent()方法不会被调用,且这种情况属于自view消耗了点击事件。而我们平时的setOnClickListener()设置的OnClickListener是在onTouchEvent()里被执行的,并且如果我们没有重写onTouchEvent()的话,onTouchEvent()会返回true。
技术分享 




二.具体情况分析
1.当ViewGroup和View都不拦截不消耗点击事件ACTION_DOWN时流程如下
技术分享 
并且之后的ACTION_MOVE和ACTION_UP都不会再传给ViewGroup和VIew,而是直接交给Activity的onTouchEvent()处理
2,若ViewGroup不拦截,在view也不消耗,然当事件再次来到ViewGroup的onTouchEvent()时,如果ViewGroup决定要消耗掉该ACTION_DOWN,则以后该TouchEvent的ACTION_MOVE和ACTION_UP都会传到该ViewGroup,并且不会调用其onInterceptTouchEvent()
技术分享 
3.当ViewGroup的onInterceptTouchEvent()拦截了ACTION_DOWN时,点击事件不再传给子view,这时候会调用ViewGroup的onTouchEvent()判断是否要消耗ACTION_DOWN,如果不消耗,则会调用Activity的onTouchEvent(),并且以后该TouchEvent系列的ACTION_MOVE和ACTION_UP都不会再传给ViewGroup即其以下的子View。
技术分享 
如果消耗,则以后该TouchEvent系列的ACTION_MOVE和ACTION_UP都会传给该ViewGroup(当不会传给其子view),并且不会再调用该ViewGroup的onInterceptTouchEvent()。
 
技术分享

4.对于ACTION_DOWN,当ViewGroup不拦截,子view决定消耗。那么该事件之后的ACTION_MOVE和ACTION_UP都会正常传进来,即先经过ViewGroup的dispatchTouchEvent(),再到onInterceptTouchEvent(),接着到子view的onTouchEvent(),这时即使不被View和ViewGroup消耗,同一事件体系的ACTION_MOVE和ACTION_UP都会正常传进来ViewGroup和View的。注意,这时当子view不消耗事件时,事件不会传给ViewGroup的onTouchEvent(),而是直接交给Activity
技术分享 
5.当ACTION_DOWN到ViewGroup时,如果ViewGroup不拦截,事件传给子view,如果子view消耗了,但当第一个ACTION_MOVE到ViewGroup时,ViewGroup拦截,这时,这个ACTION_MOVE会传给子View,触发子view的onTouchEvent,不过这时ACTION_MOVE变为ACTION_CANCEL,如果子view消耗了,则该ACTION_CANCEL消失,如果子View不消耗,则该ACTION_CANCEL传给ACtivity的onTouchEvent()。然而,不管子View消耗不消耗ACTION_CANCEL,第二个开始的ACTION_MOVE和ACTION_UP都不会再传到子view了,二是得到ViewGroup的dispatchTouchEvent(),再直接传给ViewGroup的onTouchEvent()ViewGroup决定消耗则消耗,不消耗则回传给Activity。
技术分享 
6.当ViewGroup不拦截ACTION_DOWN和ACTION_MOVE,但子view拦截了。然后ViewGroup拦截ACTION_UP,这时,ACTION_UP会以ACTION_CANCEL形式传给子view,子View处理ACTION_CANCEL则事件消失,不处理则传回给Activity。
技术分享 
三.Android内置View分析
自定义的View默认是不消耗点击事件的。而Android自带的View则不一定,要该View的clickable和longClickable同时为false才不消耗点击事件,主要有一个不为false,则要消耗点击事件。TextView默认是不消耗点击事件的,除非为它设置了onTouchListener或onClickListener。Button则是消耗点击事件的,除非把他设置为不可点击。(严格来说,事件被判断为onClick必须是ACTION_DOWN,ACTION_MOVE,ATION_UP都发生在View的大小范围内)。

技术分享
四.活动处突处理方法
1.内部拦截法:
思路:ViewGroup先不拦截点击事件,先交给子view,子view先处理,当应由ViewGroup拦截的条件成立时,把事件交给ViewGroup处理。
实现:ViewGroup在onInterceptTouchEvent()中不拦截ACTION_DOWN,交给子view。子view的dispatchTouchEvent()中遇到ACTION_DOWN时调用ViewGroup的setDisallowInterceptTouchEvent(true),即设置该TouchEvent系列的ACTION_MOVE和ACTION_UP不经过ViewGroup。然后这时候子view在onTouchEvent()一定要消耗ACTION_DOWN,否则之后的事件连ViewGroup都不会传到。好了,现在之后的ACTION_DOWN会经过ViewGroup的dispatchTouchEvent()然后直接传到子view的dispatchTouchEvent(),而不会再交给ViewGroup的onInterceptTouchEvent(),这时候子view可以根据情况消耗ACTION_MOVE,当子view觉得得把之后的点击事件交给父容器时,即调用父容器的setDisallowInterceptTouchEvent(false)。而在重写ViewGroup的时候对ACTION_DOWN之外的事件都设置为拦截,使用之后的点击事件在经过ViewGroup的dispatchTouchEvent()后就会到达其onInterceptTouchEvent(),拦截之后就给ViewGroup的onTouchEvent()处理。
逻辑图如下:
 技术分享
代码实现:
ViewGroup
技术分享 
子View
 
 技术分享


2.外部拦截
实现:ViewGroup对ACTION_DOWN不拦截,而子View要消耗ACTION_DOWN(否则这一系列的点击事件就不会再传到该ViewGroup和View)。接着对于ACTION_MOVE,如果ViewGroup不拦截,则会传给子View,如果ViewGroup想拦截,则之后的事件都会传给ViewGroup的onTouchEvent()处理。并且,ViewGroup不用去拦截ACTION_UP。
 技术分享
代码:


技术分享 
 
技术分享

3.滑动处突处理方法三:
首先要明白onInterceptTouchEvent()的返回值一般也就是dispatchTouchEvent()的返回值。所以,从dispatchTouchEvent()的返回值就可以直接做出拦截与否。而且dispatchTouchEvent()在每个事件流程都会被调用到,这一点与onInterceptTouchEvent()不同,onInterceptTouchEvent()当ViewGroup决定拦截并且在onTouchEvent里消耗了事件后,同一系列的事件是不会再经过它的。
按照这个情况,就可以做出一种更加灵活的处突处理方法,但是这种方法比上面两种麻烦多了。
思路:在ViewGroup中自己做拦截处理和调用子View的dispatchTouchEvent()传递事件。
在dispatchTouchEvent()中分别判断ACTION_DOWN,ACTION_MOVE,ACTION_UP。
当ACTION_DOWN到来时,要返回true,表示接下来的事件都要传过来。然后调用子View的dispatchTouchEvent(),把ACTION_DOWN事件给子View,子View才能有正常的逻辑。
当ACTION_MOVE到来时,如果ViewGroup要拦截,则不要调用子View的dispatchTouchEvent(),这样事件就只停留在ViewGroup而不会传到子View。如果在某个ACTION_DOWN,ViewGroup又不想拦截,而想交给子View处理,则直接调用子view的dispatchTouchEvent()。当ACTION_UP到来时,如果子View需要正常执行时,就调用其dispatchTouchEvent()。
注意不要调用super.dispatchTouchEvent(),因为这里的拦截逻辑是我们自己做的,所以甚至可以不重写ViewGroup的onInterceptTouchEvent()和onTouchEvent()。


Android事件分发解析

标签:

原文地址:http://blog.csdn.net/sggdjfkf147896325/article/details/51931811

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!