本人在搜索事件分发的文章时,偶然看到的一片博文,条例很清晰,很有启发性,决定整理下来。
事件的传递
某时某刻,你不得不亲自操刀去处理点击事件,此时你不能仅仅依靠那些正确的某某监听器来达到效果.我特别不喜欢那些
类似于明信片般简洁的答案,例如触摸事件是怎样在View体系中传递的,完全没有鸟用。
你需要更多信息,但是作为了解事件分发的起点,如果直接去了解那些底层细节也是不明智的。
下面是我的方法,也许有些冗长,但绝对能让你一步步的明白。
一些假设
我们仅仅只关注那些最重要的事件(其实是动作):DOWN,MOVE,UP和CANCEL.
一个手势起始于一个DOWN事件,后面也许会有许多MOVE事件,一个UP事件,一个CANCEL事件
当用户从屏幕上释放时,系统就知道这个手势结束了。当我谈论“手势的剩余部分时”,我的意思是指该手势
其他的后续的动作MOVE,UP,CANCEL。我会忽略多点触摸,即认为仅仅是一根手指,同样,我会假设所有的View都没有onTouchListener监听器
如上图,整个View体系非常明朗。
我们的手指在红色处按下,然后不停的滑动
默认情况
我们假设所有View的相关方法都是默认的
1.DOWN事件传到C的onTouchEvent方法,改方法返回false,表示“我并不关心这个手势”
2.因为C的onTouchEvent返回了false,DOWN事件被传到了B的onTouchEvent方法,该方法不关心该手势
同样返回false
3.后面同上。由于B的不关心,DOWN事件被传到A的onTouchEvent方法,同样返回false
由于所有的View都不关心这个事件,所以该手势后面的动作都不会传给他们了(如MOVE,UP,CANCEL)
处理事件
现在我们假设C实际上是关心该事件的,可能他被设为只可点击的,或者你自己实现了onTouchEvent
1.DOWN事件被传到C的onTouchEvent方法,你可以做你想做的事情,然后他返回true.
2.因为C通过返回true告诉系统他会接管这个手势,实际上他消费了DOWN事件,那么DOWN事件
就不会传到B和A的onTouchEvent方法了
3.同样,由于C已经接管了这个手势,那么这个手势剩下的动作都会被传到C的onTouchEventy方法
后续做动作的返回值实际上已经不重要了,但是为了连续性,应该返回Ttrue
拦截事件
现在我们要面对onInterceptTouchEvent了,这个方法存在于ViewGroup中,并不存在View中
在一个View的onTouchEvent被调用之前,这个View的祖先们是可以拦截这个事件的,也就是说
他们可以偷走这个事件。现在我们重新回顾一下“处理事件”部分
1.DOWN事件被传递到A的onInterceptTouchEvent方法中,返回false,表示他不想拦截
2.DOWN事件被传递到B的onInterceptTouchEvent方法中,同样返回false
3.DOWN事件被传递C的onTouchEvent方法中,返回true,因为C想处理该事件
4.现在改手势的下一个事件来了,即MOVE事件。MOVE事件被传到A的onInterceptTouchEvent方法中
返回false,B也会做同样的处理
5.MOVE事件传递到C的onTouchEvent方法中,就像“处理事件”部分一样
6.假设A和B的onInterceptTouchEvent方法继续返回false,该手势剩下的动作会以同样的流程进行
我们注意到即使ViewGroup的onInterceptTouchEvent方法之前已经返回了false,后续的事件(如MOVE)
仍然调用了该方法,这与onTouchEvent方法是不同的
注:如果C的onTouchEvent在这里返回了false,接下来的事就像“默认情况”部分一样,虽然B和A已经
用行动表明他们不关心该手势,但是事件还是会传给他们的onTouchEvent方法
拦截事件(续)
现在我们更近一步,我们上面说到B并不拦截DOWN事件,现在我们假设他会拦截接下来的
MOVE事件。他之所以这么做也许因为B是一个ScrollView,如果用户是轻轻敲打了一下它里面的
View,被敲打的View自然会去响应点击事件。但是,一旦用户尝试滑动,此时不再是点击了--这表示用户想要滑动
那么B就会接管这个手势了。
1.DOWN事件被传递到A的onInterceptTouchEvent,返回false
2.DOWN事件被传递到B的onInterceptTouchEvent,返回false
3.C的onTouchEvent返回true
4.MOVE事件被传递到A的onInterceptTouchEvent,返回false
5.MOVE事件被传递到B的onInterceptTouchEvent,此时用户的手指已经在屏幕上滑动了一段距离
B的onInterceptTouchEvent返回true
6.刚才的MOVE事件会被转化成一个CANCEL事件,这个CANCEL事件会被传递到C的onTouchEvent
7.接下来是下一个MOVE事件,A的onInterceptTouchEvent返回false
8.此时这个MOVE事件不会传递到B的onInterceptTouchEvent,一旦你这个方法中返回了true,他就不会再被调用了。MOVE事件会被传递到B的onTouchEvent,剩下的事情也会传递到这里
9.C不会收到该手势后续的事件(此次C一共收到DOWN和CANCEL两个事件)
下面的事情值得注意:
*如果一个ViewGroup拦截最初的DOWN事件,那么这个事件也会被传递到该ViewGroup的onTouchEvent
*另一方面,如果ViewGroup只是拦截了后面的事件(如MOVE),这个事件将会被转化成 一个CANCEL事件,然后这个CANCEL传递给之前处理过该手势的,他的孩子。这个ViewGroup会处理后面的事件(不包括这次--MOVE事件已经被转化成了CANCEL给了孩子)
谨记:
掌握了这些,你就可以走的更远
C拥有一个方法,requestDisallowInterceptTouchEvent,可以阻止B偷走他的事件
如果你确实想大干一场的话,你可以重写dispatchTouchEvent事件,当事件来临的时候,随意处理他们
原文地址:http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/(也许要翻墙哦)