浅析Activity创建DecorView

2020-04-13  本文已影响0人  Lemon666

DecorView的创建

当我们创建一个Activity时,会通过setContentView来设置我们自定义的布局文件

1. onCreate(Nullable Bundle savedInstanceState)

创建Activity的onCreate方法setContentView设置布局资源

protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //R.layout.activity_main就是我们自定义的布局文件
    setContentView(R.layout.activity_main);
}

2. Activity#setContent(@LayoutRes int layoutResID)

public void setContentView(@LayoutRes int layoutResID) {
    //getWindow()返回的对象是PhoneWindow,PhoneWindow是Window的唯一实现类
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

3. PhoneWindow#setContentView(int layoutResID)

初始化DecorView和获取mContentParent,我们就是在mContentParent里添加自定义布局

@Override
public void setContentView(int layoutResID) {
    //mContentParent是DecorView的子View,是一个id为R.id.content的FrameLayout
    if (mContentParent == null) {
        //初始化创建DecorView,mContentParent在installDecor方法内获取
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //移除所有子View,也就是我们自定义布局
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        //将我们自定义的布局文件解析后添加到mContentParent
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

4. PhoneWindow#installDecor()

初始化DecorView和获取mContentParent

// 创建DecorView
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        //生成mDecor
        //generateDecor直接实例化返回一个DecorView
        mDecor = generateDecor(-1);
        // setDescendantFocusability的参数值:
        // FOCUS_BEFORE_DESCENDANTS: 父布局会优先子布局获取焦点
        // FOCUS_AFTER_DESCENDANTS :只有当子布局不需要获取焦点时,父布局才获取焦点
        // FOCUS_BLOCK_DESCENDANTS : 覆盖子布局直接由父布局获取焦点
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        // 设置是否root节点
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        //将PhoneWindow设置到DecorView中
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        //从DecorView获取mContentParent
        mContentParent = generateLayout(mDecor);
        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
        mDecor.makeOptionalFitsSystemWindows();
        // 省略代码...
        if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
            mDecor.setBackgroundFallback(mBackgroundFallbackResource);
        }
        // 省略代码...
    }
}

5. PhoneWindow#generateDecor(int featureId)

创建DecorView

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());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    //构造DecorView
    return new DecorView(context, featureId, this, getAttributes());
}

6. PhoneWindow#generateLayout(DecorView decor)

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        // 获取Theme属性
        TypedArray a = getWindowStyle();
        // 省略解析属性代码...

        // 更具features获取匹配的layoutResource布局文件,该布局文件包含了id为content的布局
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            //滑动关闭页面
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            //如果存在FEATURE_LEFT_ICON或FEATURE_RIGHT_ICON
            if (mIsFloating) {
                //如果浮动窗口
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                //
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } 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布局文件解析并且添加到DecorView
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        //之后findViewById获取id为content的ViewGroup
        ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
        //如果为null直接报错,所以layoutResource资源中必须包含id为content的View
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        //省略部分代码...
        mDecor.finishChanging();
        //返回contentParent,mContentParent就获取完成
        return contentParent;
    }

总结

setContentView的流程大致走完,主要完成的以下几项

  1. 初始化创建DecorView
  2. 根据features选择合适的layoutResource,再解析添加到DecorView
  3. 通过findViewById获取id为content的ViewGroup,也就是mContentParent
  4. 把自定义的布局文件添加到mContentParent

这样就完成DecorView的创建流程大致就以上几个步骤,该流程主要创建了Activty已DecorView为根节点的View树,还未执行View的measure,layout和draw三大步骤。

上一篇下一篇

猜你喜欢

热点阅读