Android布局檔案的載入過程分析:Activity.setContentView()源碼分析

來源:互聯網
上載者:User

標籤:android   介面   

大家都知道在Activity的onCreate()中調用Activity.setContent()方法可以載入布局檔案以設定該Activity的顯示介面。本文將從setContentView()的源碼談起,分析布局檔案載入所涉及到的調用鏈。本文所用的源碼為android-19.

Step 1  、Activity.setContentView(intresId)

public void setContentView(int layoutResID) {getWindow().setContentView(layoutResID);initActionBar();}public Window getWindow() {return mWindow;}
該方法調用了該Activity成員的mWindow,mWindow為Window對象。Windown對象是一個抽象類別,提供了標準UI的顯示策略和行為策略。在SDK中只有PhoneWindow類實現了Window類,而Window中的setContentView()為空白函數,所以最後調用的是PhoneWindow對象的方法。

Step 2  、PhoneWindow.setContentView()

@Overridepublic void setContentView(int layoutResID) {if (mContentParent == null) {installDecor();} else {mContentParent.removeAllViews();}// 將布局檔案添加到mContentParent中mLayoutInflater.inflate(layoutResID, mContentParent);final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}}
該方法首先根據mContentParent是否為空白對mContentParent進行相應的設定。mContentParent為ViewGroup類型,若其已經初始化了,則移除所有的子View,否則調用installDecor()初始化。接著將資源檔轉成View樹,並添加到mContentParent視圖中。

Step 3、 PhoneWindow.installDecor() 
這段代碼比較長,下面的虛擬碼只介紹邏輯,讀者可自行查看源碼。

private void installDecor() {if (mDecor == null) {/*建立一個DecorView對象並設定相應的屬性。DecorView是所有View的根View*/mDecor = generateDecor();mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}}if (mContentParent == null) {/*建立mContentParent對象*/mContentParent = generateLayout(mDecor);// Set up decor part of UI to ignore fitsSystemWindows if appropriate.mDecor.makeOptionalFitsSystemWindows();mTitleView = (TextView)findViewById(com.android.internal.R.id.title);if (mTitleView != null) {mTitleView.setLayoutDirection(mDecor.getLayoutDirection());// 根據features值,設定Title的相關屬性if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {View titleContainer = findViewById(com.android.internal.R.id.title_container);if (titleContainer != null) {titleContainer.setVisibility(View.GONE);} else {mTitleView.setVisibility(View.GONE);}if (mContentParent instanceof FrameLayout) {((FrameLayout)mContentParent).setForeground(null);}} else {mTitleView.setText(mTitle);}} else {//若沒有Title,則設定ActionBar的相關屬性,如回呼函數、風格屬性mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);if (mActionBar != null) {//.......// 延遲調用invalidate,放置onCreateOptionsMenu在 onCreate的時候被調用mDecor.post(new Runnable() {public void run() {// Invalidate if the panel menu hasn't been created before this.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!isDestroyed() && (st == null || st.menu == null)) {invalidatePanelMenu(FEATURE_ACTION_BAR);}}});}}}}
建立mContentParent對象的代碼如下:

protected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.//擷取當前Window主題的屬性資料,相關欄位的檔案位置為:sdk\platforms\android-19\data\res\values\attrs.xml//這些屬性值可以在AndroidManifest.xml中設定Activityandroid:theme="",也可以在Activity的onCreate中通過//requestWindowFeature()來設定.注意,該方法一定要在setContentView之前被調用TypedArray a = getWindowStyle(); //擷取android:theme=""中設定的theme//根據主題屬性值來設定PhoneWindow的特徵與布局,包括Title、ActionBar、ActionBar的模式、Window的尺寸等屬性。//........// Inflate the window decor.// 根據上面設定的Window feature來確定布局檔案// Android SDK內建布局檔案夾位置為:sdk\platforms\android-19\data\res\layout典型的視窗布局檔案有:  R.layout.dialog_titile_icons                          R.layout.screen_title_icons  R.layout.screen_progress                             R.layout.dialog_custom_title  R.layout.dialog_title     R.layout.screen_title         // 最常用的Activity視窗修飾布局檔案  R.layout.screen_simple    //全屏的Activity視窗布局檔案int layoutResource;int features = getLocalFeatures(); //會調用requesetWindowFeature()// ......mDecor.startChanging();// 將布局檔案轉換成View數,然後添加到DecorView中View in = mLayoutInflater.inflate(layoutResource, null);decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//取出作為Content的ViewGroup, android:id="@android:id/content",是一個FrameLayoutViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {ProgressBar progress = getCircularProgressBar(false);if (progress != null) {progress.setIndeterminate(true);}}// 將頂層Window的背景、標題、Frame等屬性設定成以前的if (getContainer() == null) {//......}mDecor.finishChanging();return contentParent;}
總結:setContentView將布局檔案載入到程式視窗的過程可概況為:
1、建立一個DecorView對象,該對象將作為整個應用視窗的根視圖
2、根據android:theme=""或requestWindowFeature的設定值設定視窗的屬性,根據這些屬性選擇載入系統內建的布局檔案
3、從載入後的布局檔案中取出id為content的FrameLayout來作為Content的Parent
4、將setContentView中傳入的布局檔案載入到3中取出的FrameLayout中


最後,當AMS(ActivityManagerService)準備resume一個Activity時,會回調該Activity的handleResumeActivity()方法,
該方法會調用Activity的makeVisible方法 ,顯示我們剛才建立的mDecor視圖族。

//系統resume一個Activity時,調用此方法  final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {      ActivityRecord r = performResumeActivity(token, clearHide);      //...       if (r.activity.mVisibleFromClient) {           r.activity.makeVisible();       }  } 
makeVisible位於ActivityThread類中,代碼如下:

void makeVisible() {      if (!mWindowAdded) {          ViewManager wm = getWindowManager();   // 擷取WindowManager對象          wm.addView(mDecor, getWindow().getAttributes());          mWindowAdded = true;      }      mDecor.setVisibility(View.VISIBLE); //使其處於顯示狀況  }  

參考文章:http://blog.csdn.net/qinjuning/article/details/7226787




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.