View的事件分发

来源:互联网 发布:哆啦A梦 知乎 编辑:程序博客网 时间:2024/05/29 04:47

这里要分析的就是MotionEvent,即触摸事件。MotionEvent包括ACTION_DOWN、ACTION_UP、ACTION_MOVE等。所谓点击事件的事件分发,其实就是对MotionEvent事件的分发过程,点击事件的分发过程由三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。
先以标准的说法介绍下三个函数:


public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。


public boolean onInterceptTouchEvent(MotionEvent ev)
在上述方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么之后一系列的事件(指的是一次触摸下所发生的所有事件),该方法都不会再被调用,返回结果表示拦截当前事件。


public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。


三个方法的伪关系可以用如下的代码表示:

public boolena dispatchTouchEvent(MotionEvent event){    boolean consumed = false;    if(onInterceptTouchEvent(ev)){        consume = onTouchEvent(ev);    }else{        consume = child.dispatchTouchEvent(ev);    }    return consumed;}

大致来说明下过程:对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这是他的dispatchTouchEvent就会被调用。如果这个ViewGroup的onInterceptTouchEvent返回false,则不拦截,继续将该事件传递给他childView。如果这个ViewGroup的onInterceptTouchEvent方法返回true,则表示他要拦截当前事件,接着事件就会交给这个viewGroup的onTouchEvent处理。

好了,以上是《Android开发艺术探索》书中提到的,现在我来用大白话来解释。
dispatchTouchEvent = “轮到我了”
onInterceptTouchEvent = “要不要先给下面的人吃”
onTouchEvent = “我到底要不要吃”

好了,现在用爷爷(根ViewGroup)、爸爸(第二级ViewGroup)、儿子(最底层的ViewGroup)来说明事件分发过程。那手指触摸一系列的事件就用一次早餐的享用过程来模拟好了。

  1. 一大早,妈妈要发放食物给爷爷、爸爸、儿子当早餐。先给了爷爷一个面包(等于一个事件)。
  2. 爷爷拿到面包说了一句“轮到我了”( dispatchTouchEvent ),然后爷爷想“要不要先给下面的人吃”(onInterceptTouchEvent)。
  3. 如果爷爷想好之后给下面的人吃( onInterceptTouchEvent 返回 false,表示不拦截),然后就把面包给了爸爸。
  4. 爸爸拿到面包后说了句“轮到我了”,然后爸爸想“要不要先给下面的人吃”(onInterceptTouchEvent)。
  5. 如果爸爸想好之后给下面的人吃( onInterceptTouchEvent 返回 false,表示不拦截),然后就把面包给了儿子。
  6. 儿子拿到面包后,说了句“轮到我了”,儿子一看后面没人了(没有ChildView了),就问自己”我到底要不要吃”。
  7. 如果儿子说要吃(onTouchEvent返回true,咀嚼和消化过程在onTouchEvent中处理,哈哈,也就是处理触摸事件的时候),然后爸爸和爷爷只能看着儿子吃,他们两并没有吃(意思也就是爸爸和爷爷并没有执行onTouchEvent)。

好了,现在看下刚才的执行过程。
爷爷—dispatchTouchEvent
爷爷—onInterceptTouchEvent -> false
爸爸—dispatchTouchEvent
爸爸—onInterceptTouchEvent -> false
儿子—dispatchTouchEvent
儿子—onInterceptTouchEvent -> false
儿子—onTouchEvent -> true

到这里一个面包的分发结束了(一个事件结束了),但是在早餐中,除了面包,可能还会有其他的煎蛋牛奶什么的。
所以第一个面包可能是ACTION_DOWN,之后的可能是ACTION_MOVE,最后再上一个ACTION_UP。
后面每一个食物的发放就是重复上面的过程。

好,现在来看黑心爸爸版:
1. 一大早,妈妈要发放食物给爷爷、爸爸、儿子当早餐。先给了爷爷一个面包(等于一个事件)。
2. 爷爷拿到面包说了一句“轮到我了”( dispatchTouchEvent ),然后爷爷想“要不要先给下面的人吃”(onInterceptTouchEvent)。
3. 如果爷爷想好之后给下面的人吃( onInterceptTouchEvent 返回 false,表示不拦截),然后就把面包给了爸爸。
4. 爸爸拿到面包后说了句“轮到我了”,然后爸爸想“要不要先给下面的人吃”(onInterceptTouchEvent)。
5. 如果爸爸想好之后不给儿子吃( onInterceptTouchEvent 返回 true,表示拦截),就问自己”我到底要不要吃”。
6. 如果爸爸说要吃(onTouchEvent返回true),然后爷爷只能看着爸爸吃,爷爷并没有吃(爷爷并没有执行onTouchEvent)。

好了,现在看下刚才的执行过程。
爷爷—dispatchTouchEvent
爷爷—onInterceptTouchEvent -> false
爸爸—dispatchTouchEvent
爸爸—onInterceptTouchEvent -> true
爸爸—onTouchEvent -> true

所以后面如果再有其他食物的话,也是遵循上面的过程,但是爸爸在第一个食物分发时,说过了拦截食物,不给儿子吃,那么后面食物分发到爸爸的时候,爸爸就不会再想要不要给儿子吃(不执行onInterceptTouchEvent),直接自己吃掉(执行onTouchEvent)。所以如果总共分发了两个食物的过程是:
爷爷—dispatchTouchEvent –面包
爷爷—onInterceptTouchEvent -> false –面包
爸爸—dispatchTouchEvent –面包
爸爸—onInterceptTouchEvent -> true –面包
爸爸—onTouchEvent -> true –面包

爷爷—dispatchTouchEvent –煎蛋
爷爷—onInterceptTouchEvent -> false –煎蛋
爸爸—dispatchTouchEvent –煎蛋
爸爸—onTouchEvent -> true –煎蛋

当然,这是早饭的过程,如果中饭的话,妈妈重新分发食物的时候,爸爸的onInterceptTouchEvent还是会再执行。在一次事件序列中,一旦onInterceptTouchEvent返回true,则这个view认为本次事件序列中,该view都是拦截事件的。

再看下黑心爸爸孝顺爷爷版:
和上面的过程差不多,爸爸不给儿子吃,但是自己拿到食物也不吃,给爷爷吃,那么分发两个食物的过程是:
爷爷—dispatchTouchEvent –面包
爷爷—onInterceptTouchEvent -> false –面包
爸爸—dispatchTouchEvent –面包
爸爸—onInterceptTouchEvent -> true –面包
爸爸—onTouchEvent -> false –面包
爷爷—onTouchEvent -> true –面包

爷爷—dispatchTouchEvent –煎蛋
爷爷—onTouchEvent -> true –煎蛋

爷爷—dispatchTouchEvent –牛奶
爷爷—onTouchEvent -> true –牛奶
这里是因为爷爷第一次将食物传到后面,但是没人吃,发现又传回自己手中,所以认为后面的人不吃,如果这里爷爷吃掉了,那么后面再分发食物的话,爷爷不会再执行onInterceptTouchEvent ,只执行dispatchTouchEvent 和 onTouchEvent 。如果爷爷也不吃的话,食物还给妈妈,妈妈认为三人都不要吃,结束本次早餐,即使后面还有食物,也不会分发了。

那现在再看如果三个人都不饿,第一个面包爷爷拿到手上后都给爸爸,爸爸拿到手上给儿子,儿子拿到手上,自己不吃,还给爸爸,爸爸不吃,再还给爷爷,爷爷也不吃,还给妈妈,妈妈发现这食物没有一个人吃,就直接认为早餐的其他食物他们也不想吃,不会重复上面的过程了,本次早餐结束。过程就是:
爷爷—dispatchTouchEvent –面包
爷爷—onInterceptTouchEvent -> false –面包
爸爸—dispatchTouchEvent –面包
爸爸—onInterceptTouchEvent -> false –面包
儿子—dispatchTouchEvent –面包
儿子—onInterceptTouchEvent -> false –面包
儿子—onTouchEvent -> false –面包
爸爸—onTouchEvent -> false –面包
爷爷—onTouchEvent -> false –面包

再看一种情况,爷爷拿到食物,给了爸爸,爸爸给了儿子,儿子不吃,还给爸爸,爸爸不吃,还给爷爷,爷爷吃了。然后妈妈再给爷爷食物的时候,爷爷就不会再给爸爸,直接自己吃了,因为刚刚食物爸爸和儿子都不吃,爷爷就断定他们两后面的早饭都不吃了(不执行onInterceptTouchEvent),所以过程如下:
爷爷—dispatchTouchEvent –面包
爷爷—onInterceptTouchEvent -> false –面包
爸爸—dispatchTouchEvent –面包
爸爸—onInterceptTouchEvent -> false –面包
儿子—dispatchTouchEvent –面包
儿子—onInterceptTouchEvent -> false –面包
儿子—onTouchEvent -> false –面包
爸爸—onTouchEvent -> false –面包
爷爷—onTouchEvent -> true –面包
爷爷—dispatchTouchEvent –煎蛋
爷爷—onTouchEvent -> true –煎蛋
爷爷—dispatchTouchEvent –牛奶
爷爷—onTouchEvent -> true –牛奶

所以综上所述来看,只要事件分发到某个view上,一定会执行dispatchTouchEvent,如果不想将该事件分发给下面的,就onInterceptTouchEvent返回true,如果自己不想处理这个事件,onTouchEvent返回false。

好了,相信大家应该都很理解了,不理解再多看一遍,楼主已经用尽自己的语文水平了。

View处理事件的方式有很多种,除了上面说到的onTouchEvent方法以外,还可以通过onTouchListener.onTouch方法来。不管是哪种方法,都是处理事件,达到的效果一样。onTouch优先级比onTouchEvent高,如果onTouch函数返回true,则事件被处理,继续下一个事件的分发。如果onTouch返回false,则onTouchEvent被执行。
看个例子,如果爸爸拿到食物后,拦截了,并且采用新的方法来处理食物(打包,带到公司吃,不管怎么样,这个食物也算是被爸爸处理了),那么爸爸的onTouch函数返回true。过程如下:
爷爷—dispatchTouchEvent –面包
爷爷—onInterceptTouchEvent -> false –面包
爸爸—dispatchTouchEvent –面包
爸爸—onInterceptTouchEvent -> true –面包
爸爸—onTouch -> true –面包

爷爷—dispatchTouchEvent –煎蛋
爷爷—onInterceptTouchEvent -> false –煎蛋
爸爸—dispatchTouchEvent –煎蛋
爸爸—onTouch -> true –煎蛋

需要注意的是,每次事件到达,onTouch函数都会受到消息并调用!!!
如果这个view还设置了onClickListener,那么onClick函数钟会在 onTouchEvent函数中调用到。
举例子说明:如果这个view有onTouchListener,那么处理事件的过程如下:
如果onTouch返回true,则onTouchEvent和onClick函数都不执行。
如果onTouch返回false,则执行onTouchEvent和onClick函数。
如果这个view没有onTouchListener的话,就直接执行onTouchEvent和onClick,当然前提是有事件到达。

但是一个触摸事件,可能会有很多个MotionEvent,也会多次触发dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent,但是onClick只会执行一次,你总没看到碰一下按钮,onClick函数被执行多次吧。
当然onClick执行是有条件的,就是所有的MotionEvent都是在一个view上触摸生成的,也就是说你可能手指按着一个地方开始,然后全屏滑动(中途会离开这个view所在的区域),这是不会触发onClick的。而且onClick是在最后一个motionEvent的传递过程中被调用的。所以也就是你看到的,当抬起手的时候,才会看到view的onClick函数被执行。

还有需要注意的是,view并没有onInterceptTouchEvent,因为它没有子view,所以也没必要拦击,如果事件到view的时候,直接执行onTouchEvent,根据结果来判断是否要将该事件向上抛(儿子向爸爸抛,爸爸向爷爷抛)。

当一个点击事件产生后,它的传递过程遵循如下吮吸:Activity->window->view。

综上所述,可以得出几条结论:

  1. 同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束。整个事件序列以down事件开始,中间伴随多个move,最后以up结束。
  2. 正常情况下一个事件至多被一个view拦截且消耗。
  3. 对于onInterceptTouchEvent方法来讲,有两种情况不会调用,第一种就是第一次调这个view的onInterceptTouchEvent方法时,这个方法返回了true(黑心爸爸版),第二种,这个view的onInterceptTouchEvent返回false,但是这个事件交给后面的人都不处理,又重新将这个事件返还给他,那么这个view也默认后面的人不要处理事件,那么后面如果有新的事件到,这个view也不会调用onInterceptTouchEvent,直接调用自己的onTouchEvent。
  4. 如果一个view处理了第一个事件,但是第二个事件未处理(onTouchEvent返回false),后续的事件还是会给他处理,并不会传递到父view中,上面讲的黑心爸爸这些都是针对第一个食物的处理,如果第一个食物处理了,那么后面的食物还是会在分发到其手上。!!!!!!!!!!!!

接下来看看源码:
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

0 0
原创粉丝点击