Android视图加载到窗体的流程分析

Android视图加载到窗体的流程分析

    ONE Goal ,ONE Passion !

每天与界面打交道,也没有真正去整理过activity的界面时如何加载到窗体上的.扒扒代码看看系统是如何帮我们做的吧.

想知道布局视图加载到手机屏幕上的流程,我们首先要从acticity中的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(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }

第二: 可以看到真正执行的是getWindow().setContentView(layoutResID);
2.1:那么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.
         */

        private Window mWindow;

        public Window getWindow() {
            return mWindow;
        }
2.2:咦.mWindow怎么来的呢?Activit中的main方法中会调用attach.随之就产出了一个mWindow对象.我们的mWindow对象实际上是一个PhoneWindow.
    final void attach(...){
      mWindow = new PhoneWindow(this);
            mWindow.setCallback(this);
            mWindow.setOnWindowDismissedCallback(this);
            mWindow.getLayoutInflater().setPrivateFactory(this);

    ....
    }
2.3: 拿到了mWindow后,看看它有做了什么
   /**
     * Convenience for
     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
     * to set the screen content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the screen.
     *
     * @param layoutResID Resource ID to be inflated.
     * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
     */

    将一个布局资源文件中设置为screen content(屏幕内容),这个资源将被填充到这个screen的顶层view中.
    public abstract void setContentView(@LayoutRes int layoutResID);
2.4可以看到getWindow().setContentView(layoutResID);实际上是PhoneWindow.setContentWindow.

PhoneWindow中的setContentWindow();



    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);// 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

执行到installDecor()

 private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码
        mDecor.makeOptionalFitsSystemWindows();
        mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
        ......
        }
    }
    }

generateLayout的代码如下

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();// 获取当前设置的主题
        ......
        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的
        } 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);
        }

    //19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局,
    //这些布局在android4.2-source\frameworks\base\core\res\res\layout

        int layoutResource;
        int features = getLocalFeatures();
        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;
            }
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            layoutResource = com.android.internal.R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            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;
            }
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            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) {
                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_action_bar;
                }
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
        } else {
            layoutResource = com.android.internal.R.layout.screen_simple;
        }

        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout
        ......
        mDecor.finishChanging();
        return contentParent;
    }

这里已经完成了对布局文件的加载.

第二:再看看initWindowDecorActionBar();
 /**
     * Creates a new ActionBar, locates the inflated ActionBarView,
     * initializes the ActionBar with the view, and sets mActionBar.
     */

    创建一个新的ActionBar,去放置在ActionBarView中.将ActionBar初始化一个view.

    private void initWindowDecorActionBar() {
        Window window = getWindow();

        // Initializing the window decor can change window feature flags.
        // Make sure that we have the correct set before performing the test below.
        window.getDecorView();

        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
            return;
        }

        mActionBar = new WindowDecorActionBar(this);
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);

        mWindow.setDefaultIcon(mActivityInfo.getIconResource());
        mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
    }
第三:看一下screen两种常见布局:

默认screen布局:
android4.2-source\frameworks\base\core\res\res\layout文件夹下Screen-title.xml

 <linearlayout android:fitssystemwindows="true" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 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_height="wrap_content" android:layout_width="match_parent">
    <frameLayout
        android:layout_width="match_parent"
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <textview android:background="@null" android:fadingedge="horizontal" android:gravity="center_vertical" android:id="@android:id/title" android:layout_height="match_parent" android:layout_width="match_parent" style="?android:attr/windowTitleStyle">
    </frameLayout>
    <frameLayout android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
    </viewstub></linearlayout>

设置全屏时加载的screen布局

<linearlayout android:fitssystemwindows="true" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
    <viewstub android:id="@+id/action_mode_bar_stub" android:inflatedid="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_height="wrap_content" android:layout_width="match_parent">
    <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" />
    </viewstub></linearlayout>
第4: 我们设置布局时使用的setConentView,为什么是setContent呢?其实就是将资源文件加载到了content中.
最后总结一下:我们设置setContentView时关键就是执行了下面两行代码
//将布局填充到Content中
         getWindow().setContentView(layoutResID);
            //创建 ActionBar.
         initWindowDecorActionBar();

要睡觉了.(~﹃~)~zZ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值