xml布局显示原理

/ 0评 / 0

Activity#setContentView

当我们在Activity中使用setContentView的时候,其实非常简单的交给了Window去处理,在Android中,Window的子类只有一个PhoneWindow

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

PhoneWindow#setContentView

我们的xml文件在变成可见的控件之前,系统会调用installDecor()方法初始化一个DecorView作为整个Activity的根布局

@Override
public void setContentView(int layoutResID) {
    //检查是否存在根布局,最开始的时候为null
    if (mContentParent == null) {
        //创建DecorView
        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 {
        //使用LayoutInflater将xml转换为View,并添加到我们的根布局中,这里就将我们的布局添加到Activity上了
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Window.Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

DecorView的初始化installDecor

generateDecor()方法中new出我们的DecorView以后,则是填充系统布局了,这一步在generateLayout中完成

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        //new DecorView
        mDecor = generateDecor(-1);
        ....
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        //往DecorView里面填充系统的布局
        mContentParent = generateLayout(mDecor);
        ....
    }
}
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, this);
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

PhoneWindow#generateLayout

可以看到,首先根据各种Window Flag设置不同的属性,这也是为什么requestFeature必须在setContentView之前调用的原因,然后则是系统根据不同的flag去加载各种不同的默认布局文件,这些文件中,都有一个id为Window#ID_ANDROID_CONTENT的ViewGroup,这个布局则是用来填充我们的布局文件啦~~~,回到PhoneWindow#setContentView中,mContentParent则是这个ViewGroup,

protected ViewGroup generateLayout(DecorView decor) {
    //首先设置各种Window Flag
    ...
    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }
    ...

    //然后根据不同的Flat加载不同的系统默认布局
    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {

        layoutResource = R.layout.screen_title_icons;
    } 
    ....

    //将系统默认布局添加到Docorview上面
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    //id为ID_ANDROID_CONTENT的ViewGroup则用来包裹我们的布局
    ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
    return contentParent;
}

发表评论

您的电子邮箱地址不会被公开。