事件分发发生在ViewGroup中的dispatchTouchEvent中
action_move会触发多次
view中dispatchTouchEvent用来处理事件
viewgroup中dispatchTouchEvent用来分发事件,不处理事件,viewgroup直接交给view去处理事件
viewgroup先走分发流程,再走处理流程
view只走处理流程
点击事件的整个分发流程大概:
首先点击事件,压到屏幕后,触发触屏,导致驱动,再到内核这一块,然后再慢慢的到安卓底层,安卓底层再到源码的话,安卓是直接先到activity的dispatchTouchEvent
然后通过getWindow(window是一个抽象类,他只有一个实现类就是PhoneWindow)调用PhoneWindow的superDispatchTouchEvent方法
然后会调用DecorView的superDispatchTouchEvent
然后调用父类的dispatchTouchEvent方法,这里父类是FrameLayout,但其实是调用的viewgroup的dispatchTouchEvent方法
我们的activity,以及viewGroup容器,都是通过viewGroup的dispatchTouchEvent来进行分发的,然后再由view.dispatchTouchEvent进行处理
注意:
分发是从底层的view一层一层分发
处理是从顶层的view一层一层往上处理的
处理事件最先处理的是我们最先看到的容器,而分发流程正好相反
刚总结完了事件处理,现在来梳理一下事件分发
我们其实可以想象我们的屏幕是一个立体坐标系
首先点击事件产生时,先进入的是activity
activity的dispatchTouchEvent
然后交给PhoneWindow的superDispatchTouchEvent
然后再到DecorView的superDispatchTouchEvent
然后调用父类的dispatchTouchEvent
DecorView是继承FrameLayout的,但是FrameLayout没有重写dispatchTouchEvent,
所以就到了我们的viewgroup的dispatchTouchEvent
而viewgroup是重写了父类的dispatchTouchEvent,所以可以用来进行事件分发,而事件处理是交给了父类,也就是view
super.dispatchTouchEvent -->view.dispatchTouchEvent
继续回到事件分发
点击事件到达顶级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方法
如下:
在这里提出三个问题:
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会打印,
如果onTouch返回false呢?
这里是onTouch和onClick都会执行,而且 注意是onTouch先执行,
—>可见onTouch的返回值决定oclick是否执行
我们的事件处理在view.dispatchTouchEvent里面,主要是在这段代码里执行
最外层的if判断点击事件是否安全,一般来说我们的点击事件都是安全的。
然后判断mListenerInfo是否为空。这里我们需要看到前面的
setOnClickListener或者setOnTouchListener方法里面,
再看到getListenerInfo里面
这里有一个单例模式,如果mListenerInfo不为空那么直接返回,否则创建一个mListenerInfo。
所以mListenerInfo肯定不为空。
再看到li.mOnTouchListener != null这个判断。
这个地方其实在
调用方法的时候就已经new好了。所以这里也是不为空的。
(mViewFlags & ENABLED_MASK) == ENABLED表示是否控件是可以点击的
然后就到了li.mOnTouchListener.onTouch(this, event),在这里执行了onTouch。
如果onTouch返回了false,result就会返回false。如果onTouch返回了true,result就会返回true。
然后在下面接着又有一个判断,这里的意思是result为false的话onTouchEvent就会执行,为true就不会执行
所以我们可以推测onclick方法在onTouchEvent方法里面执行
进一步推测onTouchEvent方法不一定是执行的。当onTouch返回ture,result返回ture的时候就不会。、
再回到onclick的执行,我们可以发现log日志打印,onclick是在ACTION_UP 之后执行的,所以我们可以看到switch判断下
case MotionEvent.ACTION_UP:中的
看到这个PerformClick方法
然后进入performClickInternal方法
继续performClick方法
诶,有没有一种拨开云雾见光明的感觉
playSoundEffect(SoundEffectConstants.CLICK);//对点击声音的处理
总结顺序就是
dispatchTouchEvent —>onTouch —> onTouchEvent —> onClick
如果onTouch返回true,就不会继续了