今天coding,无意写了个小bug出来,dialog去dismiss的时候不起作用,后来排查到是在dispatchTouchEvent中去show dialog时,有可能会创建两个Dialog,然后监听回调还是第一个的,当dismiss的时候,mDocorView是null的,所以dismiss的时候,就直接return掉,没有执行dismiss的逻辑。总结来说这个bug还是因为自己对事件分发理解的不透彻,正好之前没总结过,所以就写一遍博客记录下,方便自己之后查看。
首先我们直接把View和ViewGroup一起来看,写一个demo,demo很简单,自定义两个View:一个继承Button(TestEventBtn),一个继承LinearLayout(TestEventDispatchLayout),然后这个Button放到TestEventDispatchLayout上,网上大部分的例子应该都是这个做demo的吧。之后就分别实现他们的dispatchTouch、onTouchEvent、onInterceptTouchEvent(只有ViewGroup有)、另外在Activity中把两个控件的onClickListener也加上。
1)首先我们来做做实验,通过实验肯定有些情况不明白的,再看代码。
- 点击TestEventDispatchLayout除了TestEventBtn之外的区域:
从上面的日志我们可以看出touch事件首先是在被点击到的区域传递。从Activity到TestEventDispatchLayout传递了ACTION_UP和ACTION_DOWN事件,最后被TestEventDispatchLayout的onClick把事件吃掉,我们改动下,不设置onClickListener看看会怎么样:
- 点击TestEvent:
- 点击TestEventDispatchLayout之外的区域:
上面三种情况都是从MainActivity 的dispatchTouchEvent开始的,我们可以debug抓个trace看下点击事件传递路径:
从这个堆栈,我们大致可以看出,最下面应该是硬件层会不停的检测是否有touch事件,当有touch事件时,会通过InputEventReceiver去dispatchInputEvent,接着中间这一大块都是在ViewRootImpl中的处理。
基本都是对事件的处理和传递,最后会传递到PhoneWindow的DecorView中dispatchTouchEvent,它的源码是:
熟悉代码的同学可能会对这个Callback特别熟悉,Activity就实现了这个callback,因此这里的Callback就是Activity,好了,这就验证了上面的都是从Activity的dispaTouchEvent开始了。
从网上找了一个哥们的图,如下,能比较直观的理解事件传递过程。
一个View就像一棵树一样,touch事件从根依次的传递到叶子的view。这也是android设计巧妙的地方,用了组合设计模式。
2)