Android View 显示原理分析2

164 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

3.DecorView

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        ......
        //通过Inflater解析XML布局文件,得到一个View对象
        //root就是DecorView中添加的layoutResource,即R.layout.screen_simple.xml
        final View root = inflater.inflate(layoutResource, null);
        //通过addView把布局文件添加到DecorView中
        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.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }
}

      前面说到的,执行generateLayout(DecorView decor)主要做了以下两个工作:

      1.创建对应layoutResource = R.layout.screen_simple的布局,然后将布局加载到DecorView中;

      2.从加载的布局文件中找到R.id.content控件,返回contentParent,供activity显示界面;

      再回到PhoneWindow.java中的setContentView()方法:

public void setContentView(int layoutResID) {
    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 {
        //----------分析--------------------
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

      分析:执行mLayoutInflater.inflate(layoutResID, mContentParent)解析布局并加载,将setContentView()传进来的xml布局解析加载到mContentParent上,mContentParent是R.layout.screen_simple.xml中的FrameLayout,然后将activity中setContentView()传进来的xml布局解析加载到mContentParent上。

4.简单总结

      1、在PhoneWindow中创建顶层的DecorView;

      2、在DecorView中会根据主题的不同加载一个不同的布局;

      3、从加载到DecorView中的布局中找到contentParent(即FrameLayout);

      4、把Activity中的布局解析并添加到contentParent中;

      引用大佬画的图,比较清晰,setContentView()涉及到的布局结构及布局文件如下:

image.png

5.setContentView整个执行流程

image.png

二.View的显示

      当执行到handleResumeActivity时,Activity的onResume()方法被调用,然后WindowManager会将DecorView设置给ViewRootImpl,这样DecorView就被加载到Window中了,此时界面还没有显示出来,还需要经过View的measure,layout和draw方法,才能完成View的工作流程。

      View的绘制是由ViewRootImpl来负责的,每一个DecorView都有一个与之关联的ViewRootImpl,这种关联关系是由WindowManager维护的,将DecorView和ViewRootImpl关联(通过ViewRootImpl的setView方法)之后,ViewRootImpl的requestLayout会被调用以完成初步布局,通过scheduleTraversals方法向主线程发送消息请求遍历,最终调用ViewRootImpl的performTraversals方法,这个方法会执行View的measure,layout 和draw流程。

image.png