天天看点

【Android面试】View的事件分发

目录

      • 分发的事件
      • View继承关系
      • 事件分发处理流程
      • 事件分发
        • 顶级View对点击事件的分发过程
      • 事件处理
        • 事件处理总结

分发的事件

首先事件分发主要分为

【Android面试】View的事件分发

事件分发发生在ViewGroup中的dispatchTouchEvent中

action_move会触发多次

View继承关系

【Android面试】View的事件分发

view中dispatchTouchEvent用来处理事件

viewgroup中dispatchTouchEvent用来分发事件,不处理事件,viewgroup直接交给view去处理事件

事件分发处理流程

【Android面试】View的事件分发

viewgroup先走分发流程,再走处理流程

view只走处理流程

【Android面试】View的事件分发

点击事件的整个分发流程大概:

首先点击事件,压到屏幕后,触发触屏,导致驱动,再到内核这一块,然后再慢慢的到安卓底层,安卓底层再到源码的话,安卓是直接先到activity的dispatchTouchEvent

【Android面试】View的事件分发

然后通过getWindow(window是一个抽象类,他只有一个实现类就是PhoneWindow)调用PhoneWindow的superDispatchTouchEvent方法

【Android面试】View的事件分发

然后会调用DecorView的superDispatchTouchEvent

【Android面试】View的事件分发

然后调用父类的dispatchTouchEvent方法,这里父类是FrameLayout,但其实是调用的viewgroup的dispatchTouchEvent方法

【Android面试】View的事件分发

我们的activity,以及viewGroup容器,都是通过viewGroup的dispatchTouchEvent来进行分发的,然后再由view.dispatchTouchEvent进行处理

注意:

分发是从底层的view一层一层分发

【Android面试】View的事件分发

处理是从顶层的view一层一层往上处理的

【Android面试】View的事件分发

处理事件最先处理的是我们最先看到的容器,而分发流程正好相反

事件分发

现在来梳理一下事件分发

我们其实可以想象我们的屏幕是一个立体坐标系

【Android面试】View的事件分发

首先点击事件产生时,先进入的是activity

activity的dispatchTouchEvent

【Android面试】View的事件分发

然后交给PhoneWindow的superDispatchTouchEvent

【Android面试】View的事件分发

然后再到DecorView的superDispatchTouchEvent

【Android面试】View的事件分发

然后调用父类的dispatchTouchEvent

DecorView是继承FrameLayout的,但是FrameLayout没有重写dispatchTouchEvent,

【Android面试】View的事件分发

所以就到了我们的viewgroup的dispatchTouchEvent

【Android面试】View的事件分发

而viewgroup是重写了父类的dispatchTouchEvent,所以可以用来进行事件分发,而事件处理是交给了父类,也就是view

super.dispatchTouchEvent -->view.dispatchTouchEvent

继续回到事件分发

顶级View对点击事件的分发过程

点击事件到达顶级View(一般是ViewGroup)以后,会调用ViewGroup的dispatchTouchEvent方法,主要逻辑是这样的,

如果顶级ViewGroup拦截事件也就是onInterceptTouchEvent返回ture,那么事件就交给ViewGroup处理,

(处理:这时如果ViewGroup的setOnTouchListener被调用的话,则OnTouch会被调用,否则OntouchEvent会被调用,也就是说都调用的情况下,onTouch会屏蔽掉onTouchEvent。在onTouchEvent中,如果设置了setOnClickListener,那么onClick会被调用,如果onTouch返回ture,那么不会调用onTouchEvent,也就不会调用onClick)

如果顶级ViewGroup不拦截事件,那么事件会传递给它所在的点击事件链上的子View,这时子View的dispatchTouchEvent会被调用,到此为止,事件已经从顶级View传递给了下一层View,接下来的传递过程和顶级View是一致的。如此循环就完成了整个事件的分发。

事件处理

我们先创建一个Button,然后通过点击事件调用button的onClick和onTouch方法

如下:

【Android面试】View的事件分发

在这里提出三个问题:

1.onTouch的返回值 有什么用?

2.onClick和onTouch哪个会先调用?

3.在哪里调用?

event对应事件
ACTION_DOWN             = 0;//按下
ACTION_UP               = 1;//离开
ACTION_MOVE             = 2;//滑动
ACTION_CANCEL           = 3;//事件被上层拦截
           

如果onTouch返回ture,会出现什么情况?onClick和onTouch方法中的日志都会打印吗?

答案如下:只有onTouch方法中log会打印,

【Android面试】View的事件分发

如果onTouch返回false呢?

【Android面试】View的事件分发

这里是onTouch和onClick都会执行,而且 注意是onTouch先执行,

—>可见onTouch的返回值决定oclick是否执行

我们的事件处理在view.dispatchTouchEvent里面,主要是在这段代码里执行

【Android面试】View的事件分发

最外层的if判断点击事件是否安全,一般来说我们的点击事件都是安全的。

然后判断mListenerInfo是否为空。这里我们需要看到前面的

setOnClickListener或者setOnTouchListener方法里面,

【Android面试】View的事件分发

再看到getListenerInfo里面

【Android面试】View的事件分发

这里有一个单例模式,如果mListenerInfo不为空那么直接返回,否则创建一个mListenerInfo。

所以mListenerInfo肯定不为空。

【Android面试】View的事件分发

再看到li.mOnTouchListener != null这个判断。

这个地方其实在

【Android面试】View的事件分发

调用方法的时候就已经new好了。所以这里也是不为空的。

(mViewFlags & ENABLED_MASK) == ENABLED表示是否控件是可以点击的

然后就到了li.mOnTouchListener.onTouch(this, event),在这里执行了onTouch。

如果onTouch返回了false,result就会返回false。如果onTouch返回了true,result就会返回true。

【Android面试】View的事件分发

然后在下面接着又有一个判断,这里的意思是result为false的话onTouchEvent就会执行,为true就不会执行

所以我们可以推测onclick方法在onTouchEvent方法里面执行

进一步推测onTouchEvent方法不一定是执行的。当onTouch返回ture,result返回ture的时候就不会。、

再回到onclick的执行,我们可以发现log日志打印,onclick是在ACTION_UP 之后执行的,所以我们可以看到switch判断下

case MotionEvent.ACTION_UP:中的

【Android面试】View的事件分发

看到这个PerformClick方法

【Android面试】View的事件分发

然后进入performClickInternal方法

【Android面试】View的事件分发

继续performClick方法

【Android面试】View的事件分发

诶,有没有一种拨开云雾见光明的感觉

playSoundEffect(SoundEffectConstants.CLICK);//对点击声音的处理
           

事件处理总结

总结顺序就是

dispatchTouchEvent —>onTouch —> onTouchEvent —> onClick

如果onTouch返回true,就不会继续了