View和ViewGroup的TouchEvent

转载 2015年11月19日 14:42:00

当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布局加载进去,到底加载到哪里去了?

[java] view plaincopy
  1. /** 
  2.   * Set the activity content from a layout resource.  The resource will be 
  3.   * inflated, adding all top-level views to the activity. 
  4.   * 
  5.   * @param layoutResID Resource ID to be inflated. 
  6.   * 
  7.   * @see #setContentView(android.view.View) 
  8.   * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 
  9.   */  
  10.  public void setContentView(@LayoutRes int layoutResID) {  
  11.      getWindow().setContentView(layoutResID);  
  12.      initWindowDecorActionBar();  
  13.  }  


上面注释写的很清楚,通过一个layout资源给Activity设置内容,资源将被添加到Activity最顶层的View上也就是调用了方法体中的getWindow().set方法,首先这里getWindow() 拿到的是一个Window的子类,PhoneWindow的实例,那么这个Window对象是在哪里 赋值的呢,我们在Activity中找到attach方法如下所示:

[java] view plaincopy
  1. final void attach(Context context, ActivityThread aThread,  
  2.             Instrumentation instr, IBinder token, int ident,  
  3.             Application application, Intent intent, ActivityInfo info,  
  4.             CharSequence title, Activity parent, String id,  
  5.             NonConfigurationInstances lastNonConfigurationInstances,  
  6.             Configuration config, String referrer, IVoiceInteractor voiceInteractor) {  
  7.         attachBaseContext(context);  
  8.   
  9.         mFragments.attachHost(null /*parent*/);  
  10.   
  11.         mWindow = new PhoneWindow(this);  
  12.         mWindow.setCallback(this);  
  13.         mWindow.setOnWindowDismissedCallback(this);  
  14.         mWindow.getLayoutInflater().setPrivateFactory(this);  
  15.         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {  
  16.             mWindow.setSoftInputMode(info.softInputMode);  
  17.         }  
  18.         if (info.uiOptions != 0) {  
  19.             mWindow.setUiOptions(info.uiOptions);  
  20.         }  
  21.         mUiThread = Thread.currentThread();  
  22.   
  23.         mMainThread = aThread;  
  24.         mInstrumentation = instr;  
  25.         mToken = token;  
  26.         mIdent = ident;  
  27.         mApplication = application;  
  28.         mIntent = intent;  
  29.         mReferrer = referrer;  
  30.         mComponent = intent.getComponent();  
  31.         mActivityInfo = info;  
  32.         mTitle = title;  
  33.         mParent = parent;  
  34.         mEmbeddedID = id;  
  35.         mLastNonConfigurationInstances = lastNonConfigurationInstances;  
  36.         if (voiceInteractor != null) {  
  37.             if (lastNonConfigurationInstances != null) {  
  38.                 mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;  
  39.             } else {  
  40.                 mVoiceInteractor = new VoiceInteractor(voiceInteractor, thisthis,  
  41.                         Looper.myLooper());  
  42.             }  
  43.         }  
  44.   
  45.         mWindow.setWindowManager(  
  46.                 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),  
  47.                 mToken, mComponent.flattenToString(),  
  48.                 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);  
  49.         if (mParent != null) {  
  50.             mWindow.setContainer(mParent.getWindow());  
  51.         }  
  52.         mWindowManager = mWindow.getWindowManager();  
  53.         mCurrentConfig = config;  
  54.     }  

在第11行初始化mWindow对象,这个对象是window 接口的实现类 PhoneWindow 的实例。

  那我们看一下PhoneWindow方法中的SetContentView方法代码如下所示:

[java] view plaincopy
  1. @Override  
  2. public void setContentView(int layoutResID) {  
  3.         // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window  
  4.        // decor, when theme attributes and the like are crystalized. Do not check the feature  
  5.        // before this happens.  
  6.         if (mContentParent == null) {  
  7.             installDecor();  
  8.        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
  9.            mContentParent.removeAllViews();  
  10.         }  
  11.   
  12.         if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  
  13.             final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,  
  14.                     getContext());  
  15.             transitionTo(newScene);  
  16.        } else {  
  17.            mLayoutInflater.inflate(layoutResID, mContentParent);  
  18.        }  
  19.       final Callback cb = getCallback();  
  20.         if (cb != null && !isDestroyed()) {  
  21.             cb.onContentChanged();  
  22.         }  
  23.     }  

这里我们只看下第6和第11行,首先判断mContentParent是不是 null,我们先搞明白mContentParent是什么东西?

[java] view plaincopy
  1. // This is the top-level view of the window, containing the window decor.  
  2.    private DecorView mDecor;  
  3.   
  4.     // This is the view in which the window contents are placed. It is either  
  5.     // mDecor itself, or a child of mDecor where the contents go.  
  6.    private ViewGroup mContentParent;  


通过查看源代码 我们这里知道mContentParent是一个ViewGroup对象  ,上面的注释我们可以明白这个Window中的内容就放置在mContentParent上面, 这个mContentParent或者是一个DecorView对象或者是一个DecorView的子类。

OK,搞明白了mContentParent是一个ViewGroup对象 ,那我们继续往下看

如果是installDecor()不用想我们也知道这个方法肯定是初始化了mContentParent,一起看下是不是我们想的那样吧。

[java] view plaincopy
  1. private void installDecor() {  
  2.         if (mDecor == null) {  
  3.             mDecor = generateDecor();  
  4.             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
  5.             mDecor.setIsRootNamespace(true);  
  6.             if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {  
  7.                 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);  
  8.             }  
  9.         }  
  10.         if (mContentParent == null) {  
  11.             mContentParent = generateLayout(mDecor);  
  12.   
  13.             // Set up decor part of UI to ignore fitsSystemWindows if appropriate.  
  14.         //.....  
  15.         //...... 省略若干代码  
  16.         }  
  17.     }  

  这里先判断 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这个载体上面。


举报

相关文章推荐

Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制

今天这篇文章主要分析的是Android的事件分发机制,采用例子加源码的方式让大家深刻的理解Android事件分发的具体情况,虽然网上很多Android的事件分发的文章,有些还写的不错,但是我还是决定写...

自定义View画出手机通讯录中右边字母,并设置TouchEvent/观察者模式

首先画出26 个字母1.画出26个字母首先需要一个字符数组,里面的包含了26个英文字母2.设置字母的高度为当前View的总高度除以float类型的26,设置字母显示位置,横坐标是当前View 的宽度减...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

Android 自定义 View 之处理 TouchEvent

我们都知道,自定义View的第一步是测量当前剩余空间,或者说是界面的大小,也就是measure了;然后是layout,即判断自定义view在父控件上显示的位置,这两点在上一篇通过讲解过了,所以今天我们...

android中TouchEvent事件传递之普通view之间

所谓的普通view就是ViewGroup继承LinearLayout,view继承TextView,有人说那么,button咋么办,下次再研究,这一阶段就看普通的就够了 ok下面说说我的研究结果 ...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)