2019-01-16 android之View和ViewGroup事件分发机制分析(一)(View的事件分发机制)

转自    https://blog.csdn.net/gsw333/article/details/51995391

一个View和ViewGroup都有自己的事件分发机制,都是写好了的,可能有些人就问了,既然都写好了,那还需要学习什么?直接用不就行了。没错,我给你做了一碗可以吃的便便,然后你不用自己做了,直接吃,但是你又嫌弃便便味道不好吃,那你咋搞?当然自己做了!那不就行了,存在即合理~鸡汁~如果能认真看完我这篇文章,相信肯定能对View的事件分发机制很了解了。

事件分发机制根据View和ViewGroup的不同所处理的方式也不同:

a.dispatchTouchEvent() :用来进行事件的分发,它的返回结果表示是否消耗掉当前的事件,但是它的返回结果也是受下面两个方法的影响

b.onInterceptTouchEvent():用来表示是否拦截当前事件,即是否将事件传递给下一层

c.onTouchEvent():这个方法应该都很熟悉了,对事件的处理,比如手指按下,移动,抬起,对这些动作的处理

ViewGroup是a.b.c三个方法都有,而View只有a.c 两个方法,至于原因显而易见了吧。

首先,我们先来看View的事件分发机制,我们自定义View继承ImageView(代码片段1):


我们现在看看打印结果:


从结果我们可以看出来一件事:那就是View的事件的这两个方法是先执行dispatchTouchEvent方法再执行onTouchEvent 方法的,dispatchTouchEvent是用来分发事件用的,执行它,此时,如果它直接返回false,那么它就不消费事件,那么它就不会执行onTouchEvent方法了,因为已经返回false表示它不需要消费touch事件,我们将dispatchTouchEvent方法改为如下进行测试(代码片段2):


打印结果就是:


我再说下,dispatchTouchEvent方法的返回值表示是否消费当前事件,它是用来事件分发的,相信看上面代码能看出来了,执行了dispatchTouchEvent里面对ACTION_DOWN直接返回false代表不消费不进行事件的分发,所以就不会继续super.dispatchTouchEvent()了,更别说onTouchEvent方法了,不会进行任何下一步了。如果我们没有直接返回false而是直接break,那么就没有提前结束这个方法,因为你直接retrun后方法就直接结束了,所以没有return的时候,下面就super.dispatchTouchEvent,这个里面做了些什么呢?说实话,我也没怎么看源码,毕竟我也是渣渣,它里面你可以这么认为,它里面做的操作就是:受到onTouchEvent方法的影响,什么意思呢?咋们不是没有直接return吗?所以此时dispatchTouchEvent方法还没有进行值的返回,那没有return时的返回值就是onTouchEvent的返回值了,如果onTouchEvent返回true就是代表当前View消费touch事件了,false就是代表不消费。一句话总结就是:dispatchTouchEvent方法表示是否消费事件,如果dispatchTouchEvent直接返回false那么就是不消费,我都不消费了还需要分发事件么?不需要,所以结束;如果没有返回那就代表需要进行事件分发,只是分发!分发给当前View予touch事件,但是消不消费就是根据onTouchEvent来决定了,它返回true就代表消费,那么dispatchTouchEvent就返回true。但是,细心的人会问了,我的onTouchEvent中ACTION_MOVE和ACTION_UP没有返回false,那为啥没打印执行呢?因为我们知道我们进行touch事件的时候,手指是先按下、移动、抬起的,所以touch事件的顺序是ACTION_DOWN, ACTION_MOVE, ACTION_UP。所以onTouchEvent方法里面第一个进行判断是事件是ACTION_DOWN,里面的判断是执行log打印,然后return false了,ACTION_DOWN的打印出来了再进行return的。那有人问了,ACTION_MOVE和ACTION_UP也是先log再return的啊,为啥它们就不打印Log了?因为View的事件处理中,如果第一个动作ACTION_DOWN不消费,那么其他的后续动作事件ACTION_MOVE之类的都不会再传过来给它了,所以才会没有后续的执行打印。

上面已经详细说明了dispatchTouchEvent和onTouchEvent方法之间的关系了,下面用一段代码来试试(代码片段3):


注意看上面的代码,我们让dispatchTouchEvent方法无论什么动作ACTION_DOWN还是ACTION_MOVE等等,全都返回false,那么按照我上面说的,返回false表示不消费事件,所以onTouchEvent应该是不执行的,那么我们看看打印结果对不对:

返回结果是只有一个dispatchTouchEvent  DOWN,没有执行onTouchEvent任何动作,所以印证上面我说的对了。而且也只打印了DOWN事件,也说明了我上面说的:如果不消费DOWN事件,那么后续的都不会给它了。

注意:如果我们将所有方法都用默认的,不自己return false,即(代码片段1)一样,打印结果上面已经试过,是:


我们示例是继承的ImageView,如果我们改为TextView你就会发现打印结果是:

这是为什么呢?因为View的onTouchEvent默认会消费事件(前提是它的clickable和longclickable不同时为false),所有View的longClickable都是默认false,但是clickable就不一定了,ImageView的clickable默认是true,而TextView的clickable默认是false,即TextView的clickable和longclickable同时为false了,那么它默认不会消费事件了,所以它的onTouchEvent中的DOWN动作是返回false的,默认不消费,所以才没有后续的UP事件。

好了,上面对View中的两个方法的说明已经很详细了,相信看完后能理解我所说的了,那么下面我们接下来分析一下View的onTouch方法和onTouchEvent方法的关系,onTouch方法就是setOnTouchListener里面所重写的onTouch方法,我们来重写并默认执行看看(代码片段4):


上面是onTouch方法,下面是TouchView里面的dispatchTouchEvent方法和onTouchEvent方法(代码片段5)


我们获取布局中用的我们上面写的TouchView,然后给它setOnTouchListener,然后现在打印结果:

看打印结果,先执行的是dispatchTouchEvent 方法,这个不用解释了,因为它是进行事件分发的,无论是onTouch还是onTouchEvent都需要先被分发事件才能处理事件,所以先执行dispatchTouchEvent 可以理解。然后发现onTouchEvent方法居然比onTouch方法后执行,因为确实是这样的,可以看源代码,先执行onTouch再执行onTouchEvent方法的,因为源码中onTouchEvent方法执行的前提是对onTouch方法的返回值做判断,如果返回值是false,那么就会进入onTouchEvent方法中;如果返回值是true,那么就不会进入,因为会认为事件被onTouch方法消费了,所以不再执行了,结论就是:onTouch方法的优先级高于onTouchEvent方法。如果想看源码分析的,就去看看郭神和洪洋大神的吧,他们博客做了详细的源码分析,我就不写了,毕竟我不是郭神他们啊~菜鸟路过~

上面已经说了onTouch方法优先于onTouchEvent方法,做了两个方法的联系分析,下面再来分析一下onclick事件,有人会问了,onclick事件和touch事件难道还有关系?其实想一想就知道,肯定有关系了,因为你onclick不就是触摸按钮么,点击一下就是一个ACTION_DOWN和ACTION_UP事件。对于onclick事件,它们的优先级关系是:

onTouch > onTouchEvent >onclick事件,为什么呢?因为onclick事件的执行就是在onTouchEvent里面的,下面我们看看代码(代码片段6):


设置下onClick事件,然后打印结果:


发现调用顺序是dispatchTouchEvent - onTouch - onTouchEvent - onclick,那么我上面说的onclick事件的执行就是在onTouchEvent方法里面就能说得过去了,其实源码里面onclick事件的处理是在onTouchEvent方法中的ACTION_UP的处理当中的,所以才说onclick事件在onTouchEvent中进行的。不要以为完事了,记得上面我说过的话吧:

View的onTouchEvent默认会消费事件(前提是它的clickable和longclickable不同时为false),所有View的longClickable都是默认false,但是clickable就不一定了,ImageView的clickable默认是true,而TextView的clickable默认是false,即TextView的clickable和longclickable同时为false了,那么TextView它默认不会消费事件了,所以它的onTouchEvent中的DOWN动作是返回false的,默认不消费,所以才没有后续的UP事件。

那么按照我说的,(记住我说的两个点:1.TextView的onTouchEvent方法默认返回false不消费事件;2.onClick事件的处理是在onTouchEvent方法里面的ACTION_UP动作处理里面的。那么按照我说的,如果ACTION_UP动作不执行,onclick事件就不会触发了?),现在我们将(代码片段1)继承ImageView改为继承TextView,然后再设置onclick事件即setOnClickListener:



然后打印结果:


我们看看结果,

第一个问题:TextView的onTouchEvent方法默认返回false不消费的,但是为啥DOWN和UP动作都执行了,返回的true呢?

第二个问题:为什么执行了onclick方法,TextView的onTouchEvent方法默认返回false,那么UP就不会执行,为啥还有onclick?

这两个问题的原因都是一个:因为我们设置了onclick事件即setOnclickListener,它会让当前View的clickable变为true,所以那么View的onTouchEvent方法就会默认返回true了(不懂的自己回忆我前面说过的:View的onTouchEvent默认会消费事件(前提是它的clickable和longclickable不同时为false)),所以现在一切都能解释通了。

关于View的事件分发机制就先说到这里了,差不多都说完了,应该很清晰了,如果看完了这篇文章,相信View的事件分发机制就明白了,至于要看源码解析的就去看郭神和洪洋大神的博客吧,我就懒得重复他们的了。ViewGroup的事件分发机制和View的不一样我说过的,多一个onInterceptTouchEvent方法,关于ViewGroup的事件分发机制的分析我会在后面的文章进行说明。

推荐阅读更多精彩内容