View的绘制一:View是如何被添加到屏幕窗口上的

从activity的setContentView()方法从源码的角度来解析Android绘制UI的流程。android

先来看看View是若是添加到屏幕窗口上的:bash

首先打开activity的setContentView()的方法,源码以下:app

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}复制代码

因而可知activity的setContentView()方法中是调用的getWindow()方法的setContentView。getWindow()方法中返回的是Window,Window类是一个抽象类,它只有惟一的一个实现类为PhoneWindow。全部咱们能够去PhoneWindow类中查看setContentView方法以下:ide

@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;
}复制代码

这里咱们主要关注installDecor()和 mLayoutInflater.inflate(layoutResID, mContentParent)这两个方法。首先咱们先看看installDecor()方法的实现:布局

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);  //给mDecor赋值  
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);

        ......
    }
}

复制代码

首先在instanllDecor()方法中首先对mDecor判断是否为空,mDecor即为DecorView,DecorView是一个继承自FrameLayout 的布局。若是mDecor为空,  就会调用 generateDecor(),在这个方法中建立了DecorView对象。post

而后又对mContentParent判断是否为空,若是为空则会调用generateLayout(mDecor)方法,在此方法中给mContentParent赋值,咱们再来看看generateLayout(mDecor)代码的具体实现:ui

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

    TypedArray a = getWindowStyle();

    ....... 
    //根据主题样式调用requestFeature()和setFlags()等方法。

    // Inflate the window decor. 

    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    //这里根据设置的features不一样来给layoutResource设置不一样的值。
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
        setCloseOnSwipeEnabled(true);

    ...... 此处省几百行代码

    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
        // Embedded, so no decoration is needed.
        layoutResource = R.layout.screen_simple;
        // System.out.println("Simple!");
    }

    mDecor.startChanging();
    //将layoutResource解析成view,并将view添加到DecorView中
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);  


    //这里是获取到主容器,ID_ANDROID_CONTENT为主容器的资源id,必定存在,有系统定义    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    ......

    return contentParent;
}复制代码

此方法中重点看mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);调用了DecorView的onResourcesLoaded()方法,下面查看下onResourcesLoaded()方法的实现:this

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    ......
    //将layoutResource进行解析,建立root对象
    final View root = inflater.inflate(layoutResource, null);
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,
                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root,
                new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {

        // Put it below the color views.  将root这个View对象添加到DecorView中
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}复制代码

有以上代码可见DecorView的onResourcesLoaded()方法主要作了两件事:解析layoutResource为root对象,并将root添加到DecorView中。spa

总结一下PhoneWindow中的installDecor()方法:经过generateDecor()方法建立了一个DecorView根布局。 而后调用generateLayout()方法根据不一样的主题渲染不一样的布局并添加到DecorView中。code

phoneWindow在setContentView中调用了installDecor()方法后,而后又调用mLayoutInflater.inflate(layoutResID, mContentParent);这里layoutResID是咱们activity中设置的布局id,mContentParent是id为Android系统定义的@android:id/content 布局,以下图所示。最终咱们再activity中定义的布局就是添加到mContentParent中。



总结:View是如何添加到屏幕上的?

1.系统会建立顶层布局容器DecorView,DecorView继承自FrameLayout,是PhoneWindow对象持有的一个实例,它是全部应用的顶层View,在系统内部进行初始化;

2.DecorView初始化完成以后,系统会根据应用程序的主题样式去加载一个基础容器,如NoActionBar、DarkActionBar这些主题对应不一样的基础容器,可是每一个容器都有android系统内部定义的id为android.R.content 的FrameLayout的容器。

3.开发者经过setContentView方法设置的layout布局就是添加到这个FarmeLayout容器中。

相关文章
相关标签/搜索