Activity.setContentView()源码分析

大家都知道在Activity的onCreate()中调用Activity.setContent()方法可以加载布局文件以设置该Activity的显示界面。本文将从setContentView()的源码谈起,分析布局文件加载所涉及到的调用链。本文所用的源码为android-19.

Step 1  、Activity.setContentView(intresId)

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void setContentView(int layoutResID) {  
  2.     getWindow().setContentView(layoutResID);  
  3.     initActionBar();  
  4. }  
  5.   
  6. public Window getWindow() {  
  7.     return mWindow;  
  8. }  
该方法调用了该Activity成员的mWindow,mWindow为Window对象。Windown对象是一个抽象类,提供了标准UI的显示策略和行为策略。在SDK中只有PhoneWindow类实现了Window类,而Window中的setContentView()为空函数,所以最后调用的是PhoneWindow对象的方法。

Step 2  、PhoneWindow.setContentView()

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void setContentView(int layoutResID) {  
  3.     if (mContentParent == null) {  
  4.         installDecor();  
  5.     } else {  
  6.         mContentParent.removeAllViews();  
  7.     }  
  8.     // 将布局文件添加到mContentParent中  
  9.     mLayoutInflater.inflate(layoutResID, mContentParent);  
  10.     final Callback cb = getCallback();  
  11.     if (cb != null && !isDestroyed()) {  
  12.         cb.onContentChanged();  
  13.     }  
  14. }  
该方法首先根据mContentParent是否为空对mContentParent进行相应的设置。mContentParent为ViewGroup类型,若其已经初始化了,则移除所有的子View,否则调用installDecor()初始化。接着将资源文件转成View树,并添加到mContentParent视图中。

Step 3、 PhoneWindow.installDecor() 
这段代码比较长,下面的伪代码只介绍逻辑,读者可自行查看源码。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void installDecor() {  
  2.     if (mDecor == null) {  
  3.         /*创建一个DecorView对象并设置相应的属性。DecorView是所有View的根View*/  
  4.         mDecor = generateDecor();  
  5.         mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
  6.         mDecor.setIsRootNamespace(true);  
  7.         if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {  
  8.             mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);  
  9.         }  
  10.     }  
  11.     if (mContentParent == null) {  
  12.         /*创建mContentParent对象*/  
  13.         mContentParent = generateLayout(mDecor);  
  14.   
  15.         // Set up decor part of UI to ignore fitsSystemWindows if appropriate.  
  16.         mDecor.makeOptionalFitsSystemWindows();  
  17.           
  18.         mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
  19.         if (mTitleView != null) {  
  20.       
  21.             mTitleView.setLayoutDirection(mDecor.getLayoutDirection());  
  22.             // 根据features值,设置Title的相关属性  
  23.             if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
  24.                 View titleContainer = findViewById(com.android.internal.R.id.title_container);  
  25.                 if (titleContainer != null) {  
  26.                     titleContainer.setVisibility(View.GONE);  
  27.                 } else {  
  28.                     mTitleView.setVisibility(View.GONE);  
  29.                 }  
  30.                 if (mContentParent instanceof FrameLayout) {  
  31.                     ((FrameLayout)mContentParent).setForeground(null);  
  32.                 }  
  33.             } else {  
  34.                 mTitleView.setText(mTitle);  
  35.             }  
  36.         } else {  
  37.             //若没有Title,则设置ActionBar的相关属性,如回调函数、风格属性  
  38.             mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);  
  39.             if (mActionBar != null) {  
  40.                 //.......  
  41.                   
  42.                 // 推迟调用invalidate,放置onCreateOptionsMenu在 onCreate的时候被调用  
  43.                 mDecor.post(new Runnable() {  
  44.                     public void run() {  
  45.                         // Invalidate if the panel menu hasn't been created before this.  
  46.                         PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);  
  47.                         if (!isDestroyed() && (st == null || st.menu == null)) {  
  48.                             invalidatePanelMenu(FEATURE_ACTION_BAR);  
  49.                         }  
  50.                     }  
  51.                 });  
  52.             }  
  53.         }  
  54.     }  
  55. }  
创建mContentParent对象的代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected ViewGroup generateLayout(DecorView decor) {  
  2.     // Apply data from current theme.  
  3.     //获取当前Window主题的属性数据,相关字段的文件位置为:sdk\platforms\android-19\data\res\values\attrs.xml  
  4.     //这些属性值可以在AndroidManifest.xml中设置Activityandroid:theme="",也可以在Activity的onCreate中通过  
  5.     //requestWindowFeature()来设置.注意,该方法一定要在setContentView之前被调用  
  6.     TypedArray a = getWindowStyle();   
  7.     //获取android:theme=""中设置的theme  
  8.     //根据主题属性值来设置PhoneWindow的特征与布局,包括Title、ActionBar、ActionBar的模式、Window的尺寸等属性。  
  9.     //........  
  10.       
  11.   
  12.     // Inflate the window decor.  
  13.     // 根据上面设置的Window feature来确定布局文件  
  14.     // Android SDK内置布局文件夹位置为:sdk\platforms\android-19\data\res\layout  
  15.     典型的窗口布局文件有:  
  16.           R.layout.dialog_titile_icons                          R.layout.screen_title_icons  
  17.           R.layout.screen_progress                             R.layout.dialog_custom_title  
  18.           R.layout.dialog_title     
  19.           R.layout.screen_title         // 最常用的Activity窗口修饰布局文件  
  20.           R.layout.screen_simple    //全屏的Activity窗口布局文件  
  21.       
  22.     int layoutResource;  
  23.     int features = getLocalFeatures(); //会调用requesetWindowFeature()  
  24.     // ......  
  25.     mDecor.startChanging();  
  26.     // 将布局文件转换成View数,然后添加到DecorView中  
  27.     View in = mLayoutInflater.inflate(layoutResource, null);  
  28.     decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  
  29.     //取出作为Content的ViewGroup, android:id="@android:id/content",是一个FrameLayout  
  30.     ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
  31.     if (contentParent == null) {  
  32.         throw new RuntimeException("Window couldn't find content container view");  
  33.     }  
  34.   
  35.     if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {  
  36.         ProgressBar progress = getCircularProgressBar(false);  
  37.         if (progress != null) {  
  38.             progress.setIndeterminate(true);  
  39.         }  
  40.     }  
  41.   
  42.     // 将顶层Window的背景、标题、Frame等属性设置成以前的  
  43.     if (getContainer() == null) {  
  44.         //......  
  45.     }  
  46.   
  47.     mDecor.finishChanging();  
  48.   
  49.     return contentParent;  
  50. }  
总结: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视图族。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //系统resume一个Activity时,调用此方法    
  2. final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {    
  3.     ActivityRecord r = performResumeActivity(token, clearHide);    
  4.     //...    
  5.      if (r.activity.mVisibleFromClient) {    
  6.          r.activity.makeVisible();    
  7.      }    
  8. }   
makeVisible位于ActivityThread类中,代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void makeVisible() {    
  2.     if (!mWindowAdded) {    
  3.         ViewManager wm = getWindowManager();   // 获取WindowManager对象    
  4.         wm.addView(mDecor, getWindow().getAttributes());    
  5.         mWindowAdded = true;    
  6.     }    
  7.     mDecor.setVisibility(View.VISIBLE); //使其处于显示状况    
  8. }    

参考文章: http://blog.csdn.net/qinjuning/article/details/7226787
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值