ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

setContentView是如何把布局加上去的

2019-08-07 17:44:09  阅读:324  来源: 互联网

标签:初始化 setContentView 加上 布局 mContentParent window mDecor


setContentView是如何把布局加上去的

在Android开发中,最常见的代码就是setContentView,然后传入你写的布局ID,那么布局就被加载到界面中了,系统究竟是怎么被加到界面中的,就需要通过源码来查看了。

点击setContentView方法,进去会发现调用了以下的代码

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

可以看到是通过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;
}

其实这个getWindow方法就是返回了当前的window窗口对象,而且通过注释我们还可以知道,如果当前的Activity不可见的时候,这个window对象是为空的,那么这个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.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
 /** Flag for the "options panel" feature.  This is enabled by default. */
	public static final int FEATURE_OPTIONS_PANEL = 0;
	/** Flag for the "no title" feature, turning off the title at the top
 	*  of the screen. */
	public static final int FEATURE_NO_TITLE = 1;

关于window类的源码截取了一部分,可以看到这是一个抽象类,通过注释,我们获取信息,这个类有一个唯一的实现类PhoneWindow,所以接下来我们就需要主要去分析一下这个PhoneWindow类了,去看看这个类的setContentView做了什么

@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();
    } 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);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow这个类中setContentView方法中,我们重要关注两个方法,一个是installDecormLayoutInflater.inflate(layoutResId,mContentParent);而且在调用installDecor方法的时候,还对mContentParent进行了判断,那这个mContentParent是什么呢,我们通过installDector方法点进去看一下

if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }

由于源码里面中的方法代码太长了,这里只做部分截取,这里看到,如果mDecor==null的话,我们对mDecor进行了一个初始化,初始化的方法是generateDecor(-1),我们点击去看一下

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, getContext().getResources());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

可以看到,其实这个方法就是对DecorView做了一个初始化,最后也是返回了一个DecorView

DecorView是window的一个顶层容器,继承自FrameLayout

看完这个generateDecor之后,我们在回到installDecor方法,接着往下看,

if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
}

我们发现,在初始化mDecor之后,又对mContentParent进行了初始化,那么这个mContentParent是什么,在通过generateLayour(mDecor)方法来对里面的具体实现进行探索

代码点进去有点多,在开始的时候,是通过系统内部的一些样式来进行一些特性样式的设置,这里可以略过,然后真正需要研究的代码是从注释中 Inflate the window decor开始
可以在这里看到,源码中初始化了一个int layoutResource;那么我们往下研究,发现下面就是对这个layoutResource字段进行赋值操作。简单的理解就是通过不同的样式来加载系统中一些默认的布局文件,

在对这个layoutResource字段进行赋值之后,我们重点关注两行代码,

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

第一行代码中,主要是这个mDecor.onResourcesLoaded()方法,传入了之前赋值的layoutResource字段,这个方法点进去之后发现,其实就是对这个layoutResource指定的布局进行的绘制然后设置给了mDecor,其实也就是相当于理解为给顶层DecorView设置了一个布局,而这个布局是系统内置的,可以通过样式来指定加载哪些不同的布局文件。

第二行代码中,发现进行了一个findViewById操作,那这个ID是什么,点击去看一下

/**
 * The ID that the main layout in the XML layout file should have.
 */
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

通过注释可以获取到信息就是,这个id是主要的入口布局ID,并且必须有,在获取到这个contentParent之后,这个方法就将这个对象进行了返回,那这里就很疑问了,为什么这个id一定含有,我们通过layouttResource字段来看看之前加载的系统的布局文件。我们以系统中的R.layout.screen_simple布局为例,发现他的布局是这样写的

<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"
          android:theme="?attr/actionBarTheme" />
<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>

可以看出,这有一个布局id为content的FrameLayout,所以其实我们通过findviewViewById来获取的控件就是这个FrameLayout,通过查看系统中其他的布局文件,我们都能发现有一个ID为content的FrameLayout控件。所以是shuld have

那么这里我们一层层的返回,就会发现,其实PhoneWindow类中的mContentParent就是DecorView中的一个FrameLayout,在回到setContentView方法中,在对mContentParent进行初始化完成后,调用了mLayoutInflater.inflate(layoutResID, mContentParent);方法,这里的layoutResId就是我们传进来的布局ID,然后将布局进行填充添加到界面中,这样我们的setContentView的整个工作就完成了。

总结

看一张示意图

WX20190807-154652@2x.png

我们以R.layout.screen_simple.xml为例来进行讲解,当调用setContentView的时候,系统会先对DecorView进行判断,如果为空的话就初始化,初始化完DecorView之后,在对其布局进行一个初始化,这个布局会根据开发者指定的样式来指定不同的布局,但是每一个布局文件中都会有一个id为contentFrameLayout控件,初始化完DevorView的布局的时候,也会初始化这个FrameLayout,源码中的字段就是mContentParent,在拿到这个mContentParent之后,会将我们传入的布局文件加载到这个FrameLayout中,这样我们就能看见自己写的布局文件了

标签:初始化,setContentView,加上,布局,mContentParent,window,mDecor
来源: https://blog.csdn.net/u014697083/article/details/98765284

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有