自定义View从实现到原理(二)
View的分发机制在自定义View中属于比较重要的一部分,在这之前,我们有必要了解一下Activity的组成,然后从源码的角度分析View的事件分发机制。
源码解析Activity的构成
我们都知道,点击事件使用MotionEvent来表示的,在一个点击事件发生之后,首先会传到Activity,那么我们首先要了解一下Activity的构成,在我们写Activity的时候,都会用到类似这个语句来加载对应的布局文件:
setContentView(R.layout.activity_app_start);
在setContentView这个方法中,源码有一句我们需要注意:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
****
}
是由getWindow()这个方法实现的setContentView(),那么这个getWindow()这个方法又代表着什么呢:
public Window getWindow() {
return mWindow;
}
可以看到getWindow()这个方法返回了一个Window变量mWindow,继续查看代码找到这个mWindow的赋值在哪里,在Activity的attach()方法中:
mWindow = new PhoneWindow(this);
mWindow被初始化为PhoneWindow,结合上面的
getWindow().setContentView(layoutResID);
这行代码,就可以理解为是Activity的setContentView方法是调用了PhoneWindow类中的setContentWindow()方法,我们来看一下这个方法的关键代码:
if(mContentParent == null){
installDecor();
}
****
看一下installDecor()的方法代码:
if(mDector == null) {
mDecor = generateDecor();//1
****
}
if(mContentParent == null){
mContentParent = generateLayout(mDector);//2
****
}
首先查看1处的代码,generateDector()的代码:
protect DecorView generateDecor() {
return new DecorView(getContext() , -1);
}
在这里我们创建了一个DecorView的实例,这个DecorView其实就是Activity中的根View,查看这个DecorView的代码我们可以看出他是PhoneWindow的内部类,并且继承了FrameLayout。
返回上面的代码,我们查看2中generateLayout()方法代码,我们可以知道的是,这个方法的主要作用是根据不同的情况加载不同的布局给layoutResource,类似这样几行代码:
/**
* 几种在源码中加载布局的代码
*/
//1
layoutResource = R.layout.screen_title;
//2
layoutResource = R.layout.screen_title_icons;
****
等等类似这种的代码有多种,我们就看一下第一种 R.layout.screen_title的布局代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
这个文件在Frameworks,上面的ViewStub是用来显示ActionBar的,下面的两个FrameLayout,一个是用来显示标题title,一个是用来显示内容Content。
经过阅读以上的这些源码,我们梳理一下Activity的组成:
1.Activity中包含setContentView(*)方法,用来加载布局文件;
2.Activity的setContentView()是由一个Window对象实现其自身的setContentView()完成的;
3.这个Window对象本质上来说是一个PhoneWindow对象;
4.PhoneWindow对象,设置了Activity的根View:DecorView;
5.PhoneWindow中的方法,会根据不同的情况为DecorView进行处理:
如加载了R.layout.screen_title布局,则会将DecorView分成两个区域,一个是Title一个是Content,我们平常所写的布局正是展示在ContentView部分的;
这篇就写到这里,下一篇会开始源码解析View的事件分发机制。