zoukankan      html  css  js  c++  java
  • Android布局文件的载入过程分析:Activity.setContentView()源代码分析

    大家都知道在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()

    @Override
    public 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主题的属性数据,相关字段的文件位置为:sdkplatformsandroid-19data
    esvaluesattrs.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内置布局目录位置为:sdkplatformsandroid-19data
    eslayout
    	典型的窗体布局文件有:
    		  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",是一个FrameLayout
    	ViewGroup 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




  • 相关阅读:
    HERO 3
    office的一些应用,
    网页之间的参数传弟
    一个好的数码网站
    C++遍历中删除std::hash_map元素问题
    【转】Asio与shared_ptr的一些注意事项
    delphi的字节对齐
    paypal的即时付款通知参数列表(PDT)
    vs2010下libevent的使用
    mysql 数据库 left join,right join, inner join 知识
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6999572.html
Copyright © 2011-2022 走看看