一. 学前基础(了解Activity,Window,DecorView,ViewRoot的关系):
1. Activity:
Activity只是控制生命周期和处理事件,并不负责视图控制。真正控制视图的是PhoneWindow。一个Activity包含一个PhoneWindow,PhoneWindow才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与PhoneWindow、以及View进行交互。
2. PhoneWindow:
PhoneWindow是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。PhoneWindow是Window唯一实现的子类。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。PhoneWindow 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
3. DecorView:
DecorView是FrameLayout的子类,它被认为是Android视图树的根视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。 具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:
<FrameLayout
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize">
<TextView
android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical" />
</FrameLayout>
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foreground="?android:attr/windowContentOverlay"
android:foregroundGravity="fill_horizontal|top" />
在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View,就是上面的id为content的FrameLayout中,在代码中可以通过content来得到对应加载的布局。
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);
4. ViewRoot
所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。ViewRoot继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。
5. 总结
综上所述,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。
当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。
事件分发流程如下:
硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity-->ViewGroup-->View
二.事件分发机制
1. 事件是什么?
当用户触摸屏幕时(View或ViewGroup控件),产生的一系列的Touch事件,都是以DOWN事件开始、UP事件结束,中间有无数的MOVE事件组成。
如下图所示:
2. 事件分发的本质:
Touch事件的相关细节(如发生的位置, 时间等)会被封装成MotionEvent对象, 分发的本质即: 将MotionEvent传递到某个具体的View并且处理的整个过程;
3. 事件在哪些对象之间进行传递?
Activity-->ViewGroup-->View
4. 事件分发过程由哪些方法共同完成?
dispatchTouchEvent(): 分发传递事件, 当触摸事件传递给当前的View时, 该方法被调用;
onInterceptTouchEvent(): 判断是否拦截了某个事件,该方法只存在于ViewGroup中, 在ViewGroup的dispatchTouchEvent()内部调用;
onTouchEvent(): 处理触摸事件, 在dispatchTouchEvent()内部调用;
5. Activity事件分发机制流程如下图:
6. ViewGroup事件分发机制流程如下图:
7. View事件分发机制流程如下图:
三.总结
纸上得来终觉浅, 绝知此事要躬行;
参考流程图多看几次源码, 加深理解及多实践就ok了 !