Android 的消息分发,可分为两部分,一部分是在服务器端的分发(在各种Service中),一部分是在Client端的分发(以ViewRoot为开始)。
本文只分析Client端点击消息的分发,其他的与此类似。
背景介绍。创建一个Activity,Activity上有一个水平方向的LinearLayout,这个Layout上放了一个ListView和一个GridView结构如图所示。ListView和GridView各Node都是继承TextView来进行简单显示。
想实现功能:GridView上按鼠标左键拖动时,能实时画出拖动的矩形。
实现原理:在GridView中重写onTouchEvent(MotionEvent event)方法,计算出矩形的位置,然后调用postInvalidate()进行绘制
问题:如果拖动先在Item以外的区域,则画矩形没有问题,如果拖动的开始是在Item上的话,则画矩形出现问题,简单调查后发现,在Item上点击拖动时,不会走到GridView的onTouchEvent方法,也就是说Item将事件截获,并进行了处理。同样在Item上的点击事件也不会走到GridView的onTouchEvent方法。
现在以GridView的点击为例,说明整个调用的过程。
1,ViewRoot 中先走到 handleMessage()这里会调用到mView.dispatchTouchEvent(event)。即是调用到mView的dispatchTouchEvent(),这个mView其实是PhoneWindow中的DecorView。
2,DecorView的dispatchTouchEvent(event)方法很简单,一般而言我们会走到 cb.dispatchTouchEvent(ev) 而cb 是Callback,这个Callback是什么呢?就是我们的Activity。
3,Activity的dispatchTouchEvent(ev)中有这么一句 getWindow().superDispatchTouchEvent(ev),这里的getWindow 返回的是PhoneWindow,而它的实现是mDecor.superDispatchTouchEvent(event)。
4,DecorView的superDispatchTouchEvent(event) 实现是super.dispatchTouchEvent(event);而这个调用走到了ViewGroup 的dispatchTouchEvent(MotionEvent ev);因为DecorView是继承的FrameLayout,所以其实调用的实现类是FrameLayout。
5,FrameLayout-ViewGroup()的dispatchTouchEvent(MotionEvent ev) 会查询它的子View,并调用子View 的dispatchTouchEvent(ev),在我们这个例子中,就是调用到了LinearLayout的dispatchTouchEvent(ev)。
6,LinearLayout-ViewGroup()的dispatchTouchEvent(MotionEvent ev)继续查询它的子View,并继续调用子View 的dispatchTouchEvent(ev),这时调用到了GridView的dispatchTouchEvent(ev),GridView此函数的实现也在ViewGroup()中。
7,GridView-ViewGroup()的dispatchTouchEvent(MotionEvent ev)终于把消息发送到了根结点的View TextView。TextView 的dispatchTouchEvent(MotionEvent ev)是由View来实现的。
8,TextView-View()的dispatchTouchEvent(MotionEvent ev),在这里TextView响应了点击事件,并返回了true;
返回true之后,我们第7步中有
解决方法TextView 重写onTouchEvent 方法,返回false;将点击事件交给GridView来整体管理。