Android View體系(六)從源碼解析Activity的構(gòu)成
前言
本來這篇是要講View的工作流程的,View的工作流程主要指的measure、layout、draw這三大流程,在講到這三大流程之前我們有必要要先了解下Activity的構(gòu)成,所以就有了這篇文章。
1.從源碼解析Activity的構(gòu)成
當(dāng)我們寫Activity時(shí)會調(diào)用setContentView()方法,來加載布局,來看看setContentView()方法是怎么實(shí)現(xiàn)的(Activity.java):
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
這里調(diào)用了getWindow().setContentView(layoutResID),getWindow()指的是什么呢?接著往下看,getWindow()返回mWindow
public Window getWindow() { return mWindow; }
在Activity的attach()方法發(fā)現(xiàn)mWindow:
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 /*parent*/); mWindow = new PhoneWindow(this); ...省略 }
原來mWindow指的就是PhoneWindow,PhoneWindow是繼承抽象類Window的,這樣就知道getWindow()得到的是一個(gè)PhoneWindow,我們來看看PhoneWindow.java的setContentView()方法(PhoneWindow.java)
@Override public void setContentView(View view, ViewGroup.LayoutParams params) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { mContentParent.addView(view, params); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
在第5行看到了 installDecor()方法,來看看這個(gè)方法里寫了什么:
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); } ...省略 } ...省略 }
再接著追蹤看看generateDecor()方法里寫了什么:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
這里創(chuàng)建了一個(gè)DecorView,這個(gè)DecorView就是Activity中的根View。接著查看DecorView的源碼,發(fā)現(xiàn)DecorView是PhoneWindow類的內(nèi)部類,并且繼承FrameLayout。我們再來看看第10行g(shù)enerateLayout()方法:
protected ViewGroup generateLayout(DecorView decor) { ...省略 //根據(jù)不同的情況加載不同的布局給layoutResource int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = com.android.internal.R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); ...省略 mDecor.startChanging(); //將layoutResource加載到View中并添加到DecorView中 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ...省略 }
第42行加載layoutResource的布局,來看看其中的一種布局R.layout.screen_title,這個(gè)文件在frameworks\base\core\res\res\layout目錄中(screen_title.xml)
<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>
上面的ViewStub是用來顯示ActionBar的,下面的兩個(gè)FrameLayout,一個(gè)是title用來顯示標(biāo)題,一個(gè)是content用來顯示內(nèi)容。
2.圖解Activity的構(gòu)成
看到如上的源碼大家就知道了一個(gè)Activity包含一個(gè)window對象,這個(gè)對象是由PhoneWindow來實(shí)現(xiàn)的,PhoneWindow將DecorView做為整個(gè)應(yīng)用窗口的根View,而這個(gè)DecorView又將屏幕劃分為兩個(gè)區(qū)域一個(gè)是TitleView一個(gè)是ContentView,而我們平常做應(yīng)用所寫的布局正是展示在ContentView中的。
浙公網(wǎng)安備 33010602011771號