美文网首页
浅析Activity创建DecorView

浅析Activity创建DecorView

作者: Lemon666 | 来源:发表于2020-04-13 15:24 被阅读0次

    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三大步骤。

    相关文章

      网友评论

          本文标题:浅析Activity创建DecorView

          本文链接:https://www.haomeiwen.com/subject/ipzgmhtx.html