美文网首页
setContentView源码分析

setContentView源码分析

作者: 码字农民工 | 来源:发表于2018-06-04 18:33 被阅读60次

    setContentView源码分析

    基于AppCompatDelegateImplV9

    找到切入点

    public class MainActivity extends AppCompatActivity {
        private GLPanorama mGLPanorama;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //分析这里的源码
            setContentView(R.layout.activity_main);
        }
    }
    

    点击setContentView来到AppCompatActivity类

    @Override
        public void setContentView(@LayoutRes int layoutResID) {
             //获取代理实现类
            getDelegate().setContentView(layoutResID);
        }
    
    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
            return create(activity, activity.getWindow(), callback);
        }
    
        public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
            return create(dialog.getContext(), dialog.getWindow(), callback);
        }
    
        /*
         * 这里是重点,通过判断sdk版本来加载不同的实现类
         * 我们会点击每个实现类会发现它们最终都继承了AppCompatDelegateImplV9类
         */
        private static AppCompatDelegate create(Context context, Window window,
                AppCompatCallback callback) {
            if (Build.VERSION.SDK_INT >= 24) {
                return new AppCompatDelegateImplN(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 23) {
                return new AppCompatDelegateImplV23(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 14) {
                return new AppCompatDelegateImplV14(context, window, callback);
            } else if (Build.VERSION.SDK_INT >= 11) {
                return new AppCompatDelegateImplV11(context, window, callback);
            } else {
                return new AppCompatDelegateImplV9(context, window, callback);
            }
        }
    

    我们直接来到AppCompatDelegateImplV9类

     @Override
        public void setContentView(View v) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            contentParent.addView(v);
            mOriginalWindowCallback.onContentChanged();
        }
    
        /*
         * 因为我们在外部传入的是id,所以会走到这个方法
         */
        @Override
        public void setContentView(int resId) {
             //这里是入口
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            //把我们的布局放到contentParent里面
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }
    
        @Override
        public void setContentView(View v, ViewGroup.LayoutParams lp) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            contentParent.addView(v, lp);
            mOriginalWindowCallback.onContentChanged();
        }
    
    
    private void ensureSubDecor() {
              //因为我们在onCreate中调用setContentView,所以这里的mSubDecorInstalled为false
            if (!mSubDecorInstalled) {
                 //这里去进行创建我们的根布局
                mSubDecor = createSubDecor();
                CharSequence title = getTitle();
                if (!TextUtils.isEmpty(title)) {
                    onTitleChanged(title);
                }
    
                applyFixedSizeWindow();
    
                onSubDecorInstalled(mSubDecor);
    
                mSubDecorInstalled = true;
    
                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                if (!isDestroyed() && (st == null || st.menu == null)) {
                    invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
                }
            }
        }
    
     private ViewGroup createSubDecor() {
            TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
    
            if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
                a.recycle();
                //如果不设置AppCompat主题,这里就会抛出异常
                throw new IllegalStateException(
                        "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
            }
            //初始化相关特征标志
            if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
                  //一般我们的主题默认都是NoTitle
                  //这里我们就会发现为什么我们设置activity全屏需要在setContentView之前
                requestWindowFeature(Window.FEATURE_NO_TITLE);
            } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
                // Don't allow an action bar if there is no title.
                requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
            }
            if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
                requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
            }
            if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
                requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
            }
            mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
            a.recycle();
            
            //通过PhoneWindow创建DecorView
            mWindow.getDecorView();
    
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            ViewGroup subDecor = null;
            if (!mWindowNoTitle) {
                ...因为是NoTitle主题,所以不会进入这里
            } else {
                //这里去创建我们的subDecor布局
                if (mOverlayActionMode) {
                    ///调用了requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY)会走进来
                    subDecor = (ViewGroup) inflater.inflate(
                            R.layout.abc_screen_simple_overlay_action_mode, null);
                } else {
                    subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
                }
                    ...
            }
    
            if (subDecor == null) {
                throw new IllegalArgumentException(
                        "AppCompat does not support the current theme features: { "
                                + "windowActionBar: " + mHasActionBar
                                + ", windowActionBarOverlay: "+ mOverlayActionBar
                                + ", android:windowIsFloating: " + mIsFloating
                                + ", windowActionModeOverlay: " + mOverlayActionMode
                                + ", windowNoTitle: " + mWindowNoTitle
                                + " }");
            }
    
            if (mDecorContentParent == null) {
                mTitleView = (TextView) subDecor.findViewById(R.id.title);
            }
    
            ViewUtils.makeOptionalFitsSystemWindows(subDecor);
            
            //这里的contentView就是我们编写的布局的父布局,我们从下面的代码可以看出来。
            final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                    R.id.action_bar_activity_content);
            //这里的windowContentView是我们原来的布局的父容器
            final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
            if (windowContentView != null) {
                //在这里对原来的windowContentView里面的布局进行替换给contentView
                while (windowContentView.getChildCount() > 0) {
                    final View child = windowContentView.getChildAt(0);
                    windowContentView.removeViewAt(0);
                    contentView.addView(child);
                }
                //将原来windowContentView的id由原来的android.R.id.content设置为View.NO_ID
                windowContentView.setId(View.NO_ID);
                //这里将contentView的id设置为android.R.id.content
                contentView.setId(android.R.id.content);
    
                if (windowContentView instanceof FrameLayout) {
                    ((FrameLayout) windowContentView).setForeground(null);
                }
            }
            //在这里就将我们的subDecor添加给了PhoneWindow
            mWindow.setContentView(subDecor);
    
            contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
                @Override
                public void onAttachedFromWindow() {}
    
                @Override
                public void onDetachedFromWindow() {
                    dismissPopups();
                }
            });
    
            return subDecor;
        }
    

    我们来到PhoneWindow这个类

        @Override
        public void setContentView(View view) {
            setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    
        /*
         * 我们主要看这个方法
         */
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            if (mContentParent == null) {
                //进行DecorView的初始化
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
            //是否有transitions动画。没有,进入else
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                view.setLayoutParams(params);
                final Scene newScene = new Scene(mContentParent, view);
                transitionTo(newScene);
            } else {
                //重要!!将这个subDecor添加到这个mContentParent里面了
                //mContentParent是FrameLayout,在之前设置的View.NO_ID
                mContentParent.addView(view, params);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    
        private void installDecor() {
            if (mDecor == null) {
                //创建DecorView
                mDecor = generateDecor(-1);
                ...
            } 
            ...
            if (mContentParent == null) {
                //将DecorView传入进行进行mContentParent的初始化一系列操作
                mContentParent = generateLayout(mDecor);
                ....
            }
        }
    

    generateDecor(-1)

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

    generateLayout(mDecor)

        protected ViewGroup generateLayout(DecorView decor) {
            TypedArray a = getWindowStyle();
            //设置一堆标志位...
            ...
            mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
            
            if (!mForcedStatusBarColor) {
                //获取主题状态栏的颜色
                mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
            }
            if (!mForcedNavigationBarColor) {
                //获取底部NavigationBar颜色
                mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
            }
            //获取主题一些资源
           ...
            int layoutResource;
            int features = getLocalFeatures();
            // System.out.println("Features: 0x" + Integer.toHexString(features));
            if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                ...我们设置不同的主题以及样式,会采用不同的布局文件...
            } else {
                // Embedded, so no decoration is needed.
                layoutResource = R.layout.screen_simple;
                // System.out.println("Simple!");
            }
    
            mDecor.startChanging();
            //把screen_simple放到了DecorView里面
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
            ...
            return contentParent;
        }
    

    走到这里我们就知道了Actiity的setContentView的大部分的主要流程,创建DecorView,设置DecorView的ContentView,并将我们AppCompatDelegateImplV9中创建的subDecor里面的contentView的id替换成DecorView的ContentView的id。接下来就该将我们的布局添加至AppCompatDelegateImplV9中创建的subDecor的contentView中。

        @Override
        public void setContentView(int resId) {
            ensureSubDecor();
            //找到SubDecor中的contentView
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            //将我们的布局添加到contentParent中
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }
    

    从这里我们就会发现MainActivity.setContentView()->AppCompatDelegateImplV9.setContentView()->ensureSubDecor()->createSubDecor()->PhoneWindow.setContentView()->installDecor()->generateDecor()->generateLayout()->mContentParent.addView(view, params)->AppCompatDelegateImplV9.subDecor.contentView.addView()


    相关文章

      网友评论

          本文标题:setContentView源码分析

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