深入解析setContentView

    在Android开发中,通常,为了指定某个Activity的布局,我们都会在其生命周期函数的开始处,指定Activity的UI布局,生命周期如下:

 

     通常,可以在onCreate函数中使用setContentView指定当前Activity的UI,如果我们没有该步骤,那么Activity最终的效果上面第二个图,空空荡荡,什么都没有,只有一个Actionbar.通过setContentView传入一个布局文件,就可以将布局显示到Activity中,但是,这里面的细节如何实现?这里就进行一个分析:

         MainActivity#setContentView-->ActionBarActivity#setContentView-->Activity#setContentView:

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID); //1
        initWindowDecorActionBar(); //初始化Actionbar
}
     其中getWindow()返回的是一个抽象类Window,所以要看其实现的代码,需要看子类PhoneWindow中的实现。
public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor(); //1 
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent); //2
        }
      
    }

   1中的代码功能:因为mContentParent为null,说明当前的Activity还没有父布局,所以为当前的Activity安装一个布局,然后调用2处的代码。

mLayoutInflater.inflate(layoutResID, mContentParent);此处代码说明,setContentView是将我们定义的布局放到以mContentParent为父布局的布局中,那么由此可知,mContentParent必定包括了Actionbar等一些边框,样式之类的。进入installDecor();

    PhoneWindow#installDecor:  这里代码非常的长,省去不重要的:

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();  //产生一个装饰布局。为FrameLayout
            mDecor.setIsRootNamespace(true);  //设定根命名空间
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); //为mContentParent产生一个布局,布局的基础是上面产生的mDecor布局
            …
			这里都是进行了一些对我们选择的主题和样式的解析
        }
    }

     PhoneWindow#generateLayout:

protected ViewGroup generateLayout(DecorView decor) {
        TypedArray a = getWindowStyle();  //获取window的主题样式属性

		//下面就是根据对样式的属性设置Activity的属性
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }

       . . .

        if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        . . . 

        final Context context = getContext();
        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                R.bool.target_honeycomb_needs_options_menu);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);

        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        } else {
            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        }

        // Inflate the window decor.  实例window的布局

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
       	//下面会根据Activity的各种特性,选择默认的布局,这里我们使用下面的特性 
        } 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 = R.layout.screen_progress;
            // System.out.println("Progress!");
    	//使得   layoutResource = R.layout.screen_progress;  其中R.layout.screen_progress为系统默认的布局文件
        mDecor.startChanging();  //

		//实例化刚才设定的布局文件
        View in = mLayoutInflater.inflate(layoutResource, null);
		//将刚才实例化的布局添加到décor(前面说了,décor是一个FrameLayout)
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

		//返回当前布局的索引 在后面的布局文件中可以看到 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        
        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }//设定背景
            mDecor.setWindowBackground(background);

            //设定别的特性
        }
		//将contentParent返回
        return contentParent;
    }

   Android-sdk\platforms\android-21\data\res\layout\screen_progress.xml   布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
    <!-- 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" />

    <RelativeLayout android:id="@android:id/title_container" 
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
    >
        <ProgressBar android:id="@+android:id/progress_circular"
            style="?android:attr/progressBarStyleSmallTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dip"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:visibility="gone"
            android:max="10000"
        />
        <ProgressBar android:id="@+android:id/progress_horizontal"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="2dip"
            android:layout_alignParentStart="true"
            android:layout_toStartOf="@android:id/progress_circular"
            android:layout_centerVertical="true"
            android:visibility="gone"
            android:max="10000" 
        />
        <TextView android:id="@android:id/title"
            style="?android:attr/windowTitleStyle"
            android:layout_width="match_parent" 
            android:layout_height="match_parent"
            android:layout_alignParentStart="true"
            android:layout_toStartOf="@android:id/progress_circular"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:scrollHorizontally="true" 
        />
    </RelativeLayout>
    <FrameLayout android:id="@android:id/content"  //这个地方要添加我们自己的布局 也是一个Framelayout
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay"
    />
</LinearLayout>
    结论:通过分析,可以知道在onCreate函数中使用setContentView添加的布局文件,最总会通过系统的解析添加到一个window布局中,window布局是系统默认会提供的,也就是说,即便是我们没有给Activity添加任何布局,系统也会有一个默认的布局,只是这个布局的content为空,而我们添加的布局也最后被放在content中。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值