Android setContentView()解读

来源:互联网 发布:淘宝信用贷款怎么审查 编辑:程序博客网 时间:2024/06/06 01:56

本篇分享的目的是了解Activity的setContentView方法的执行原理,使我们更深层次的理解Activity呈现视图内容的过程。

setContentView方法简介:

/**

     * Set the activity content from a layout resource.  The resource will be

     * inflated, adding all top-level views to the activity.

     *

     * @param layoutResID Resource ID to be inflated.

     * 

     * @see #setContentView(android.view.View)

     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)

     */

    public void setContentView(int layoutResID) {

        getWindow().setContentView(layoutResID);

        initActionBar();

    }

 

直接翻译就是:

设置Activity内容布局的现实,这个布局来自项目的资源(res)文件夹、这个布局文件将会被初始化成一个View,被添加到Activity最顶层的View中。

 

查看Activity级别的源代码,如上的源代码可知,具体设置layoutResID的操作,是在getWindow()方法返回的对象执行,查看getWindow方法实现如下:

 

/**

     * Retrieve the current {@link android.view.Window} for the activity.

     * This can be used to directly access parts of the Window API that

     * are not available through Activity/Screen.

     * 

     * @return Window The current window, or null if the activity is not

     *         visual.

     */

    public Window getWindow() {

        return mWindow;

    }

 

mWindow 是 Window类的实例,Activity源代码是这样定义的:

private Window mWindow;

那么Window类的功能都有哪些呢,顾名思义Window是窗口的意思,也就说Activity所有直面用户界面其实是Window类的对象,设置包含按键处理,这由Window类定义的注释可知

/**

 * Abstract base class for a top-level window look and behavior policy.  An

 * instance of this class should be used as the top-level view added to the

 * window manager. It provides standard UI policies such as a background, title

 * area, default key processing, etc.

 *

 * <p>The only existing implementation of this abstract class is

 * android.policy.PhoneWindow, which you should instantiate when needing a

 * Window.  Eventually that class will be refactored and a factory method

 * added for creating Window instances without knowing about a particular

 * implementation.

 */

public abstract class Window {

}


由定义可知 Window类是抽象类型,不能直接去创建实例,很多抽象方法需要实现的,那么我们查看Activity中是如何去实例化mWindow对象的?

最简单直接的方法,Ctrl+F 查找Activity源代码中,只有一个地方是实例化mWindow对象的,如下源代码:

 

 final void attach(Context context, ActivityThread aThread,

            Instrumentation instr, IBinder token, int ident,

            Application application, Intent intent, ActivityInfo info,

            CharSequence title, Activity parent, String id,

            NonConfigurationInstances lastNonConfigurationInstances,

            Configuration config) {

 

        ……

 

        mWindow = PolicyManager.makeNewWindow(this);

 

        mWindow.setCallback(this);

       ……

 

由此源代码我们可以知道,它是由PolicyManager类makeNewWindow(this)方法去实例化的,下面目的就很明确了,去查看PolicyManager源代码就可以了。

PolicyManager:  com.android.internal.policy.PolicyManager  此类是来自于 com.android.internal.policy 包下面的。

很遗憾的是我电脑上的,android.jar 4.4 源代码没有此类的代码,给大家推荐一个查看源代码的网址:http://www.grepcode.com/

直接在搜索框输入:com.android.internal.policy.PolicyManager 进行搜索,选择源码4.4 版本的,就可以得到此代源代码,

查看PolicyManager.makeNewWindow(this)如下:

// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
     return sPolicysPolicy.makeNewWindow(context);
}

 

由此源代码可知,sPolicy是真正创造Window类对象的实例,那么顺藤摸瓜继续追踪,可知sPolicy 定义如下:

 private static final IPolicy sPolicy

而IPolicy 是一个接口,不可能是具体的实现的,那么就查看sPolicy 是如何在PolicyManager被初始化的,sPolicy是在PolicyManager初始代码块被初始化的,代码如下:


static {

 // Pull in the actual implementation of the policy at run-time

    try {
          Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
          sPolicysPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {

……


由如上的源代码可知sPolicy对象,是由反射的类创建的实例,而反射的路径就是 常量:POLICY_IMPL_CLASS_NAME=“com.android.internal.policy.impl.Policy”;

思考: 这里为什么要用反射的方式来做?

那么我们就查看 “com.android.internal.policy.impl.Policy” 类的源代码,还是从http://www.grepcode.com/上查询。

我们很容易就找到了Policy类makeNewWindow方法,实现如下:

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

 

至此我们知道了,Activity中呈现视图显示的对象其实是:PhoneWindow(Window的子类)类的实例

好了,同样的操作我们进入 PhoneWindow 源代码,查看它的setContentView方法是如何实现,

 

@Override

    public void setContentView(int layoutResID) {

        if (mContentParent == null) {

            installDecor();

        } else {

            mContentParent.removeAllViews();

        }

        //添加程序员自定义Layout到mContentParent

        mLayoutInflater.inflate(layoutResID, mContentParent);

        final Callback cb = getCallback();

        if (cb != null && !isDestroyed()) {

            cb.onContentChanged();

        }

    }

 由mContentParent定义可知它其实是一个ViewGroup对象,由其变量命名的意义可以知道,它应该是Activity具体内容布局View的父容器。

如果此时是空的,就执行installDecor(),方法实现如下:

private void installDecor() {

     //mDecor是DecorView类,而DecorView是FrameLayout子类,这里可以理解为FrameLayout

        if (mDecor == null) {

         //generateDecor()方法就是为了创建一个DecorView对象

            mDecor = generateDecor();

            //设置其与本身与子View可获得焦点的顺序

           mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

            //是否属于根空间

            mDecor.setIsRootNamespace(true);

            

            //菜单的相关设置

            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {

                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);

            }

        }

        

        if (mContentParent == null) {

         //此方法配置了Window样式、为mDecor添加容器框架、返回自定义内容的布局View

         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());

                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 {

                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);

                if (mActionBar != null) {

                    mActionBar.setWindowCallback(getCallback());

                    if (mActionBar.getTitle() == null) {

                        mActionBar.setWindowTitle(mTitle);

                    }

                    final int localFeatures = getLocalFeatures();

                    if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {

                        mActionBar.initProgress();

                    }

                    if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {

                        mActionBar.initIndeterminateProgress();

                    }

 

                    final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(

                            com.android.internal.R.id.action_bar_overlay_layout);

                    if (abol != null) {

                        abol.setOverlayMode(

                                (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);

                    }

 

                    boolean splitActionBar = false;

                    final boolean splitWhenNarrow =

                            (mUiOptions &ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;

                    if (splitWhenNarrow) {

                        splitActionBar = getContext().getResources().getBoolean(

                                com.android.internal.R.bool.split_action_bar_is_narrow);

                    } else {

                        splitActionBar = getWindowStyle().getBoolean(

                                com.android.internal.R.styleable.Window_windowSplitActionBar,false);

                    }

                    final ActionBarContainer splitView = (ActionBarContainer) findViewById(

                            com.android.internal.R.id.split_action_bar);

                    if (splitView != null) {

                        mActionBar.setSplitView(splitView);

                        mActionBar.setSplitActionBar(splitActionBar);

                        mActionBar.setSplitWhenNarrow(splitWhenNarrow);

 

                        final ActionBarContextView cab = (ActionBarContextView) findViewById(

                                com.android.internal.R.id.action_context_bar);

                        cab.setSplitView(splitView);

                        cab.setSplitActionBar(splitActionBar);

                        cab.setSplitWhenNarrow(splitWhenNarrow);

                    } else if (splitActionBar) {

                        Log.e(TAG"Requested split action bar with " +

                                "incompatible window decor! Ignoring request.");

                    }

 

                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||

                            (mIconRes != 0 && !mActionBar.hasIcon())) {

                        mActionBar.setIcon(mIconRes);

                    } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&

                            mIconRes == 0 && !mActionBar.hasIcon()) {

                        mActionBar.setIcon(

                                getContext().getPackageManager().getDefaultActivityIcon());

                        mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;

                    }

                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||

                            (mLogoRes != 0 && !mActionBar.hasLogo())) {

                        mActionBar.setLogo(mLogoRes);

                    }

 

                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu

                    // being called in the middle of onCreate or similar.

                    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);

                            }

                        }

                    });

                }

            }

        }

    }

 

重点方法-generateLayout()


0 0