Android源码分析-深入理解setContentView方法

来源:互联网 发布:微商加粉王软件下载 编辑:程序博客网 时间:2024/05/16 11:01

一般我们都是这样使用setContentView:

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }

但setContentView是怎么把我们的布局加载显示到界面上的呢?我们进去setContentView看一下:

    public void setContentView(int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

这里的getWindow返回一个Window对象mWindow,然后调用其setContentView方法,不过Window是一个抽象类,因此这个mWindow一定是Window的某个子类对象(你可以打印它的Class对象直接看是什么类型),它在Activity的attach方法(这个方法在ActivityThread启动Activity时会被调用)中被初始化:

final void attach(Context context, ActivityThread aThread,            Instrumentation instr, IBinder token, int ident,        ......        mWindow = PolicyManager.makeNewWindow(this);           ......

再看PolicyManager的makeNewWindow方法:

    public static Window makeNewWindow(Context context) {        return sPolicy.makeNewWindow(context);    }

这个sPolicy是通过反射创建的,是一个com.android.internal.policy.impl.Policy对象,它的makeNewWindow方法:

    public Window makeNewWindow(Context context) {        return new PhoneWindow(context);    }

到这里就可以确定Activity的getWindow()返回的其实是一个PhoneWindow对象,注意PhoneWindow所在的包为com.android.internal.policy.impl。也就是说getWindow().setContentView(layoutResID);调用的就是PhoneWindow的setContentView方法:

    @Override    public void setContentView(int layoutResID) {        if (mContentParent == null) {            installDecor();        } else {            mContentParent.removeAllViews();        }        //把我们的布局添加到mContentParent中        mLayoutInflater.inflate(layoutResID, mContentParent);        //这个cb其实就是我们的Activity对象        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }

代码逻辑并不复杂,就是把我们的布局添加到mContentParent(ViewGroup对象)中。它的赋值是在installDecor中进行的:

private void installDecor() {        if (mDecor == null) {            mDecor = generateDecor();            ......        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);            ......        }    }

先看mDecor是一个什么东西:

    protected DecorView generateDecor() {        return new DecorView(getContext(), -1);    }

原来是一个DecorView对象,DecorView类继承自FrameLayout,它是PhoneWindow的内部类。
再来看generateLayout方法:

protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.        TypedArray a = getWindowStyle();        if (false) {            System.out.println("From style:");            String s = "Attrs:";            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="                        + a.getString(i);            }            System.out.println(s);        }        mIsFloating = a.getBoolean(com.android.internal.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(com.android.internal.R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {            requestFeature(FEATURE_ACTION_MODE_OVERLAY);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus,                false)) {            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,                false)) {            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,                getContext().getApplicationInfo().targetSdkVersion                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));        }        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,                    mFixedWidthMajor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,                    mFixedWidthMinor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,                    mFixedHeightMajor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,                    mFixedHeightMinor);        }        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 targetHcNeedsOptions = context.getResources().getBoolean(                com.android.internal.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);        }        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion                >= android.os.Build.VERSION_CODES.HONEYCOMB) {            if (a.getBoolean(                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,                    false)) {                setCloseOnTouchOutsideIfNotSet(true);            }        }        WindowManager.LayoutParams params = getAttributes();        if (!hasSoftInputMode()) {            params.softInputMode = a.getInt(                    com.android.internal.R.styleable.Window_windowSoftInputMode,                    params.softInputMode);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,                mIsFloating)) {            /* All dialogs should have the window dimmed */            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;            }            if (!haveDimAmount()) {                params.dimAmount = a.getFloat(                        android.R.styleable.Window_backgroundDimAmount, 0.5f);            }        }        if (params.windowAnimations == 0) {            params.windowAnimations = a.getResourceId(                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);        }        // The rest are only done if this window is not embedded; otherwise,        // the values are inherited from our container.        if (getContainer() == null) {            if (mBackgroundDrawable == null) {                if (mBackgroundResource == 0) {                    mBackgroundResource = a.getResourceId(                            com.android.internal.R.styleable.Window_windowBackground, 0);                }                if (mFrameResource == 0) {                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);                }                if (false) {                    System.out.println("Background: "                            + Integer.toHexString(mBackgroundResource) + " Frame: "                            + Integer.toHexString(mFrameResource));                }            }            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);        }        // Inflate the window decor.        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);        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {            // If no other features and not embedded, only need a 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.dialogTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                layoutResource = com.android.internal.R.layout.screen_action_bar;            } else {                layoutResource = com.android.internal.R.layout.screen_title;            }            // System.out.println("Title!");        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;        } else {            // Embedded, so no decoration is needed.            layoutResource = com.android.internal.R.layout.screen_simple;            // System.out.println("Simple!");        }        mDecor.startChanging();        View in = mLayoutInflater.inflate(layoutResource, null);        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        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);            }        }        // Remaining setup -- of background and title -- that only applies        // to top-level windows.        if (getContainer() == null) {            Drawable drawable = mBackgroundDrawable;            if (mBackgroundResource != 0) {                drawable = getContext().getResources().getDrawable(mBackgroundResource);            }            mDecor.setWindowBackground(drawable);            drawable = null;            if (mFrameResource != 0) {                drawable = getContext().getResources().getDrawable(mFrameResource);            }            mDecor.setWindowFrame(drawable);            // System.out.println("Text=" + Integer.toHexString(mTextColor) +            // " Sel=" + Integer.toHexString(mTextSelectedColor) +            // " Title=" + Integer.toHexString(mTitleColor));            if (mTitleColor == 0) {                mTitleColor = mTextColor;            }            if (mTitle != null) {                setTitle(mTitle);            }            setTitleColor(mTitleColor);        }        mDecor.finishChanging();        return contentParent;    }

这个方法主要做两件事:
1.设置当前窗口的主题样式
2.查找适当的布局作为mDecor的子View,Android中定义了许多这种布局,在~\sdk\platforms\android-19\data\res\layout目录下可以找到,比如一般使用的screen_simple.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    android:orientation="vertical">    <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" />    <!-- 这个就是mContentParent,我们的布局添加做为它的子View -->    <FrameLayout         android:id="@android:id/content"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:foregroundInsidePadding="false"         android:foregroundGravity="fill_horizontal|top"         android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

因此平时我们调用setContentView设置显示我们的布局,实际的布局会是这样子:

这里写图片描述

实际的xml布局文件(采用screen_simple.xml为例):

<com.android.internal.policy.impl.PhoneWindow$DecorView    android:layout_width="match_parent"    android:layout_height="match_parent">    <!-- screen_simple.xml -->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:fitsSystemWindows="true"        android:orientation="vertical">        <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" />        <!-- mContentParent实际就是这个FrameLayout -->        <FrameLayout             android:id="@android:id/content"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:foregroundInsidePadding="false"             android:foregroundGravity="fill_horizontal|top"             android:foreground="?android:attr/windowContentOverlay">             <!-- 我们的activity_main.xml -->             <LinearLayout                android:layout_width="match_parent"                android:layout_height="match_parent"                android:orientation="vertical">                <TextView                     android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:text="hello world"/>                </LinearLayout>                  </FrameLayout>    </LinearLayout> </com.android.internal.policy.impl.PhoneWindow>

setContentView时序图:

这里写图片描述

转载请注明出处:http://blog.csdn.net/u012619640/article/details/50722306

1 0