自定義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的事件分發機制。