当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的ViewGroup, TouchEvent最先到达最顶层 viewGroup 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个viewGroup 的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个viewGroup 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
布局文件最底层(先后顺序)
ViewGroup:
dispatchTouchEvent 分发触摸事件,如果return true ,后面onInterceptTouchEvent onTouch包括上层View的Touch都不会触发。
return false,后面onInterceptTouchEvent onTouch包括上层View的Touch也都不会触发。
return super..后续可以触发。
onInterceptTouchEvent 拦截触摸事件,如果return true ,view中Touch不会触发,只触发ViewGroup中的onTouch事件;
return false,view中的onTouch触发,不触发ViewGroup的onTouch;
return super.... ,点击View上的时候,触发View中的onTouch()(所有事件down,up,move);(OnTouch返回的是super)点击Viewgroup上的触发ViewGroup上的onTouch()只一次down事件。
onTouch触发事件 当onInterceptTouchEvent返回true,同下;
当onInterceptTouchEvent返回super....,如果return true,down,up,move等都会触发(onInterceptTouchEvent只触发down事件)
如果return false,同下;
如果return super....,只触发down,不往下传
View中没有onInterceptTouchEvent 方法。view中触发onTouch都会触发ViewGroup中的dispatchTouchEvent ,onInterceptTouchEvent 。
分发事件的方法在Activity、View以及ViewGroup中各自存在 ,如图1表所示
图一
这样的话又牵扯到了三者之间的关系,那索性先理清楚Activity与另外两者的关系 ,在去分析触摸事件比较好。
什么是Activity 、View 、 Window?
Activity:是Android 四大组件之一, 是存放View对象的容器,也是我们界面的载体,可以用来展示一个界面。它有一个SetContentView()方法 ,可以将我们定义的布局设置到界面上。
View:就是一个个视图的对象,实现了KeyEvent.Callback和Drawable.Callback。
Window:是一个抽象类,是一个顶层的窗口,它的唯一实例是PhoneWindow它提供标准的用户界面策略,如背景、标题、区域,默认按键处理等。
分析下三者之间的关系吧
View包含很多,TextView 、Imageview 、Listview 、 Button..就是一个一个展示不同图形的对象。我们可以把view通过xml布局,或者通过new View(),然后通过addview方法或动态或静态添加到Activity的布局上。我们都知道我们定义了layout布局,通过SetContentView就可以设置到Activity上,而Activity中的SetContentView()方法,又调用了Window的SetContentView方法,也就是View通过Activity最终添加到了Window上面。
那我们今天就看一下这个方法到底如何把layout布局加载进去,到底加载到哪里去了?
在第11行初始化mWindow对象,这个对象是window 接口的实现类 PhoneWindow 的实例。
那我们看一下PhoneWindow方法中的SetContentView方法代码如下所示:
这里我们只看下第6和第11行,首先判断mContentParent是不是 null,我们先搞明白mContentParent是什么东西?
OK,搞明白了mContentParent是一个ViewGroup对象 ,那我们继续往下看
如果是installDecor()不用想我们也知道这个方法肯定是初始化了mContentParent,一起看下是不是我们想的那样吧。
这里先判断 mDecor是不是null,如果是,初始化mDecor,然后判断mContentParent是不是null,如果是,通过mDecor去初始化 mContentParent对象。 对吧,跟我们想的一样是去初始化。
OK ,这里创建出了mContentParent对象,我们接着看PhoneWindow的SetContentView方法的第11行,这里先进行了判断,具体判断 我们先不关心,我们继续往下执行在看第12行或者17行,我们就清楚了我们在Activity中设置的layoutid 在这里加载到了mContentParent 上面。也就是所有的所有的View 对象都是加载到了mContentParent对象上面,而我们前面知道mContentParent是根据DecorView而来的,这样我们就清楚了Activity与Window以及View的关系,这里用图2 表示一下他们的关系。
看图识关系
图二
对着这张图,打个比喻来帮助理解。
Activity就像是一扇贴着窗花的窗口,Window就想上窗口上面的玻璃,而View对象就像一个个贴在玻璃上的窗花。
最后的Activity与Window View的关联在画一个图3:
图三
总结起来说就是 Activity会调用PhoneWindow的setContentView()将layout布局添加到DecorView上,而此时的DecorView就是那个最底层的View。然后通过LayoutInflater.infalte()方法加载布局生成View对象并通过addView()方法添加到Window上,(一层一层的叠加到Window上)所以,Activity其实不是显示视图,Window才是真正的显示视图。
注:一个Activity构造的时候只能初始化一个Window(PhoneWindow),另外这个PhoneWindow有一个View容器 mContentParent,这个
View容器是一个ViewGroup,是最初始的跟视图,然后通过addView方法将View一个个层叠到mContentParent上,这些层叠的View最终放在Window这个载体上面。