美文网首页安卓总结
Android事件机制(二)

Android事件机制(二)

作者: 雨天飞0114 | 来源:发表于2016-08-11 10:26 被阅读76次

    关于Android事件机制,很多大神都做了分析,我也是通过他们的帖子进行学习,在此特向他们表达谢意。
    Android事件机制(一)从代码演示的角度整理了一下我对Android事件传递的理解,下面就通过源码浅显的分析一下个人理解(想想源码,就觉任重而道远,笑cry)。

    那么来看一下上篇中提到的,“有触摸,就有dispatchTouchEvent方法的调用”,这个dispatchTouchEvent方法。
    虽然dispatchTouchEvent方法是事件分发的第一步,但是一般情况下不太会去改写这个方法,只是onInterceptTouchEvent、onTouchEvent、OnTouchListener.onTouch这几个方法都会被其以不同的条件调用,这也就决定了事件如何传递。

    点击MyView,首先会调用到MyView最高父辈的dispatchTouchEvent方法,这里从MyViewGroupA.dispatchTouchEvent开始,而MyViewGroupA继承自RelativeLayout,因此我们进入到了ViewGroup.dispatchTouchEvent

    当事件到来的时候,ViewGroup.dispatchTouchEvent会先判断是否要拦截事件:

    ViewGroup.dispatchTouchEvent

    从2094行可以看出,事件机制从MotionEvent.ACTION_DOWN开始,此时会重置所有状态。
    从2014行开始,对是否拦截的boolean变量进行判断赋值。

    如果不拦截:

    ViewGroup.dispatchTouchEvent

    从2164-2221行就会对子View进行遍历,找到处于点击范围的合适的子View,找到之后,通过2197行的dispatchTransformedTouchEvent方法来调用child的dispatchTouchEvent方法,并获取它的返回值,根据返回的值来判断子View是否成功消耗了事件,如果返回的是true代表成功消费,那么就会对mFirstTouchTarget进行赋值,从而不会进入2238行的条件判断,不会调用super的dispatchTouchEvent方法,事件也就停止了传递:

    ViewGroup.dispatchTouchEvent

    如果child没有消耗事件,即child的dispatchTouchEvent方法返回了false,或者没有发现合适的子View,即child == null,那么就不会给mFirstTouchTarget进行赋值,即mFirstTouchTarget == null,通过dispatchTransformedTouchEvent方法就会调用到super的dispatchTouchEvent方法。

    如果进行了拦截,那么直接通过2238行进而执行2240行,调用super.dispatchTouchEvent方法,自己处理事件。

    dispatchTransformedTouchEvent的部分源码如下:

    dispatchTransformedTouchEvent

    经过上述分析,可以知道:
    如果不设置拦截,MyViewGroupA的直属child是MyViewGroupB,因此MyViewGroupA就会调用到MyViewGroupB的dispatchTouchEvent方法,而MyViewGroupB的直属child是MyView,所以就会调用到View的dispatchTouchEvent方法。
    假设MyViewGroupA或者MyViewGroupB设置了事件拦截,那么就会调用ViewGroup的super.dispatchTouchEvent,而ViewGroup继承自View,所以最终还是调用了View的dispatchTouchEvent方法。

    那么接下来就看一下View.dispatchTouchEvent方法:

    View.dispatchTouchEvent

    可见,上面说的onTouchEvent、OnTouchListener.onTouch会被dispatchTouchEvent调用,不是瞎掰的。
    通过黄框圈中的代码可以很明显的看出View.dispatchTouchEvent的返回值受onTouchEvent、OnTouchListener.onTouch影响,并且OnTouchListener.onTouch优先于onTouchEvent

    源码就先看到这里,来捋一下上篇中Log_1所示的流程执行:

    Log_1

    点击MyView,触发ACTION_DOWN
    (1) 首先调用MyViewGroupA.dispatchTouchEvent,在MyViewGroupA.dispatchTouchEvent中调用MyViewGroupA.onInterceptTouchEvent,返回false,不拦截事件;
    (2) 找到MyViewGroupA的子View,即MyViewGroupB,调用MyViewGroupB.dispatchTouchEventMyViewGroupB.dispatchTouchEvent在执行中调用MyViewGroupB.onInterceptTouchEvent,返回false,不拦截事件;
    (3) 找到MyViewGroupB的子View,即MyView,调用MyView.dispatchTouchEvent,由于没有设置OnTouchListener,因此在MyView.dispatchTouchEvent中就会调用MyView.onTouchEvent,而MyView.onTouchEvent这个方法返回false,因此就使MyView.dispatchTouchEvent返回false,也就是说,MyView没有消费这个事件;
    (4) 接着,MyViewGroupB.dispatchTouchEvent中接收到MyView返回的结果,发现MyView没有消费事件,mFirstTouchTarget就不会被赋值,即mFirstTouchTarget == null,那么根据ViewGroup.dispatchTouchEvent源码的第2238行,就会去调用super.dispatchTouchEvent,进而调用到MyViewGroupB.onTouchEvent这个方法,而这个方法返回false,因此就使MyViewGroupB.dispatchTouchEvent返回false,也就是说,MyViewGroupB也没有消费这个事件;
    (5) 最后,MyViewGroupA.dispatchTouchEvent中接收到MyViewGroupB返回的结果,发现MyViewGroupB没有消费事件,mFirstTouchTarget就不会被赋值,即mFirstTouchTarget == null,那么根据ViewGroup.dispatchTouchEvent源码的第2238行,就会去调用super.dispatchTouchEvent,进而调用到MyViewGroupA.onTouchEvent这个方法,自己处理和消费事件。
    (注:这个过程画图应该会更清晰,这里我是按照自己的语言分析顺序写的,文末的参考链接中有图,而且很清晰)

    在事件序列中,总是以ACTION_DOWN开始,如果不是ACTION_DOWN,说明事件序列已经开始传递了。如果对ACTION_DOWN不消费,那么就可以理解为,我不要这个事件,也就没有了接下来ACTION_MOVE、ACTION_UP的传递和处理。

    这一篇就先分析到这里,太长了看着会厌烦,下一篇分析一下onTouchEvent返回true的情况。

    参考:
    Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
    Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
    Android事件分发机制详解
    Android群英传-事件拦截机制分析

    相关文章

      网友评论

        本文标题:Android事件机制(二)

        本文链接:https://www.haomeiwen.com/subject/ljljsttx.html