美文网首页
Android UI绘制分析(一)-解析布局资源,完成布局

Android UI绘制分析(一)-解析布局资源,完成布局

作者: 张小凡凡 | 来源:发表于2019-07-08 17:55 被阅读0次

    本文源码基于 Android sdk 26, 为了逻辑清晰,省略了无关代码,不排除后期重新加上相关代码

    当用户在activity中 调用setContentView 方法时,将自己的布局id 设置到activity中。而activity通过内部持有的window对象 来设置。

       /**
         * Set the activity content from a layout resource.  The resource will be  inflated, 
         * adding all top-level views to the activity.
    
         * @param layoutResID Resource ID to be inflated.
         */
        public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    

    getWindow()方法 返回 内部持有的window对象,其具体实例是PhoneWindow。

     mWindow = new PhoneWindow(this, window, activityConfigCallback);
    
    phoneWindow 实现setContentView操作

    显然,我们需要看看phoneWindow中setContentView做了什么。

        @Override
        public void setContentView(int layoutResID) {
    
            if (mContentParent == null) {
                installDecor();
            } 
            mLayoutInflater.inflate(layoutResID, mContentParent);
            mContentParent.requestApplyInsets();
        }
    

    mContentParent 实际上就是一个ViewGroup, setContentView方法实际上就把设置的布局资源解析成View后,再添加到 mContentParent中,完成整个布局。so,这个mContentParent从何而来,

    1. 根据Activity主题配置,创建DecorView

    phoneWindow的setContentView 方法中,首先调用installDecor(),初始化DecorView , 和mContentParent。

    private void installDecor() {
            if (mDecor == null) {
                //创建 DecorView
                mDecor = generateDecor(-1);
            } 
            if (mContentParent == null) {
               //解析mContetnParent
                mContentParent = generateLayout(mDecor);
            }
    }
    
    

    而DecorView 本质上就是一个FrameLayout。

    public class DecorView extends FrameLayout
    

    我们先看如何初始化DecorView。

        protected DecorView generateDecor(int featureId) {
            return new DecorView(context, featureId, this, getAttributes());
        }
    

    嗯,很简单。 我们再看如何初始化 mContentView

    protected ViewGroup generateLayout(DecorView decor) {
            int layoutResource;
            int features = getLocalFeatures();
            //根据activity主题中设置的参数 获取不同的系统内置的布局资源id
            if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                layoutResource = R.layout.screen_swipe_dismiss;
    
            }  else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = R.layout.screen_simple_overlay_action_mode;
    
            } else {
                layoutResource = R.layout.screen_simple;
            }
            //将获取到布局资源id 添加到mDecor中
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    
            //系统内置全部布局资源中 都会一个id为ID_ANDROID_CONTENT 的 ViewGroup,获取到并赋值给contentParent
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
            return contentParent;
        }
    
    
    2. 解析用户设置的view,并添加到DecorView中

    在上面方法中,第一步,会根据当前activity的主题信息 加载不同的布局资源,并添加到DecorView中。
    第二步,从布局资源中获取 id为 ID_ANDROID_CONTENT 的view 并赋值给contentParent 返回,这个view便是上文中 mContentParent,在之后,会将用户自己的布局 添加到mContentParent中,从而达到界面显示的目的。

        /**
         *DecorView 直接解析根据主题配置 获取到的系统布局id,并添加到自己身上
         */
        void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
            final View root = inflater.inflate(layoutResource, null);
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    

    所以最终一个Activity界面显示的布局应该是这样的:

    activity界面显示布局视图.png
    总结

    所以一个View是如何被添加到屏幕窗口上的呢。

    1. 创建顶层布局容器DecorView
    2. 在顶层布局中加载基础布局ViewGroup
    3. 将ContentView添加到基础布局中的FrameLayout中

    相关文章

      网友评论

          本文标题:Android UI绘制分析(一)-解析布局资源,完成布局

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