当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布局加载进去,到底加载到哪里去了?
-
-
-
-
-
-
-
-
-
- public void setContentView(@LayoutRes int layoutResID) {
- getWindow().setContentView(layoutResID);
- initWindowDecorActionBar();
- }
上面注释写的很清楚,通过一个layout资源给Activity设置内容,资源将被添加到Activity最顶层的View上也就是调用了方法体中的getWindow().set方法,首先这里getWindow() 拿到的是一个Window的子类,PhoneWindow的实例,那么这个Window对象是在哪里 赋值的呢,我们在Activity中找到attach方法如下所示:
- final void attach(Context context, ActivityThread aThread,
- Instrumentation instr, IBinder token, int ident,
- Application application, Intent intent, ActivityInfo info,
- CharSequence title, Activity parent, String id,
- NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
- attachBaseContext(context);
-
- mFragments.attachHost(null );
-
- mWindow = new PhoneWindow(this);
- mWindow.setCallback(this);
- mWindow.setOnWindowDismissedCallback(this);
- mWindow.getLayoutInflater().setPrivateFactory(this);
- if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
- mWindow.setSoftInputMode(info.softInputMode);
- }
- if (info.uiOptions != 0) {
- mWindow.setUiOptions(info.uiOptions);
- }
- mUiThread = Thread.currentThread();
-
- mMainThread = aThread;
- mInstrumentation = instr;
- mToken = token;
- mIdent = ident;
- mApplication = application;
- mIntent = intent;
- mReferrer = referrer;
- mComponent = intent.getComponent();
- mActivityInfo = info;
- mTitle = title;
- mParent = parent;
- mEmbeddedID = id;
- mLastNonConfigurationInstances = lastNonConfigurationInstances;
- if (voiceInteractor != null) {
- if (lastNonConfigurationInstances != null) {
- mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
- } else {
- mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
- Looper.myLooper());
- }
- }
-
- mWindow.setWindowManager(
- (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
- mToken, mComponent.flattenToString(),
- (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
- if (mParent != null) {
- mWindow.setContainer(mParent.getWindow());
- }
- mWindowManager = mWindow.getWindowManager();
- mCurrentConfig = config;
- }
在第11行初始化mWindow对象,这个对象是window 接口的实现类 PhoneWindow 的实例。
那我们看一下PhoneWindow方法中的SetContentView方法代码如下所示:
- @Override
- public void setContentView(int layoutResID) {
-
-
-
- if (mContentParent == null) {
- installDecor();
- } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- mContentParent.removeAllViews();
- }
-
- if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
- getContext());
- transitionTo(newScene);
- } else {
- mLayoutInflater.inflate(layoutResID, mContentParent);
- }
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();
- }
- }
这里我们只看下第6和第11行,首先判断mContentParent是不是 null,我们先搞明白mContentParent是什么东西?
-
- private DecorView mDecor;
-
-
-
- private ViewGroup mContentParent;
通过查看源代码 我们这里知道mContentParent是一个ViewGroup对象 ,上面的注释我们可以明白这个Window中的内容就放置在mContentParent上面, 这个mContentParent或者是一个DecorView对象或者是一个DecorView的子类。
OK,搞明白了mContentParent是一个ViewGroup对象 ,那我们继续往下看
如果是installDecor()不用想我们也知道这个方法肯定是初始化了mContentParent,一起看下是不是我们想的那样吧。
- private void installDecor() {
- if (mDecor == null) {
- mDecor = generateDecor();
- mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mDecor.setIsRootNamespace(true);
- if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
- mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
- }
- }
- if (mContentParent == null) {
- mContentParent = generateLayout(mDecor);
-
-
-
-
- }
- }
这里先判断 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这个载体上面。