AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上a.Activity中在onCreate中调用setContentView(R.layout.main)是做什么的;b.PhoneWindow;c.DecorView;1.setContentView()调用流程ActivityextendsAppCompatActivity@OverrideprotectedvoidonCreate(BundlesavedInst…

大家好,又见面了,我是你们的朋友全栈君。

注意:AppCompatActivity.setContentView()与Activity.setContentView()主要的区别,Activity.setContentView直接将视图添加到Window上,AppCompatActivity.setContentView()借助AppCompatActivity的Delegate代理类,将要显示的视图加入到代理层视图,代理层视图在添加到Window上;

目录

1.setContentView()调用流程

1)在AppCompatActivity定义了setContentView

2)getDelegate()

3)ensureSubDecor();

4)createSubDecor()

5)mWindow.getDecorView()

6)Window.setContentView()

2.整个添加在Activity中添加视图的流程如下

3.学习总结


大概可以了解如下内容:

a.AppCompatActivity中在onCreate中调用setContentView(R.layout.main)是做什么的,如何装载视图;

b.PhoneWindow是什么,与Window有什么关系;

c.DecorView如何被创建的,与Window和其他视图是什么关系;

d.在我们调用requestFeature的时候为什么要在setContentView之前?

1.setContentView()调用流程

Activity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);//android.R.id.content
}

1)在AppCompatActivity定义了setContentView

调用添加视图方法;

    //设置要显示视图的布局ID或者View
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

实现了三个重载的setContentView方法,getDelegate()方法负责创建Activity的代理类实例,然后调用setContentView方法添加显示的视图,Activity通过代理模式添加要显示的视图;

2)getDelegate()

负责创建Activity代理AppCompatDelegate类实例;

AppCompatDelegate
    //创建Activity代理类实例
    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);
    }

    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 {
            return new AppCompatDelegateImplV14(context, window, callback);
        }
    }
通过代码可以发现根据Android版本不同会实现不同的具体代理类;

代理类的继承关系如下:

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

具体在代理类AppCompatDelegateImplV9中实现setContentView()方法

 @Override
    public void setContentView(View v) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v);
        mOriginalWindowCallback.onContentChanged();
    }

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        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();
    }

ensureSubDecor();//负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上(mWindow.setContentView(subDecor);)

ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);//获取mSubDecor下的视图做为父视图
contentParent.removeAllViews();//移除父视图下的所有自视图
contentParent.addView(v);//将Activity下setContentView设置的视图添加到父视图上
mOriginalWindowCallback.onContentChanged();

setContentView调用流程如下:

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

3)ensureSubDecor();

检测mSubDecor视图是否已经创建,否则负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;

rivate void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

mSubDecorInstalled表示mSubDecor视图是否创建,没创建则调用createSubDecor()方法创建;

4)createSubDecor()

负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;

 private ViewGroup createSubDecor() {
        //获取主题数组
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
        //AppCompatActivity需要设置Theme.AppCompat主题,否则抛出异常
        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        //1.初始化视图显示相关特征
        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
            //样式没有Title
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {           //样式显示ActionBar
            // 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();

        //确认Window上是否已经安装DecorView,没有则创建DecorView并添加到Window上
        
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;//依据相关参数设置创建subDecor,并添加到Window上

        //2.上面说了主题默认都是NoTitle,所以不会走里面的方法
        if (!mWindowNoTitle) {
            if (mIsFloating) {
                // 如果是弹框Dialog,则加载弹框视图
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);

                // 浮动窗口没有ActionBar,重设置标志
                mHasActionBar = mOverlayActionBar = false;
            } else if (mHasActionBar) {//有ActionBar
                /**
                *这需要一些解释。因为我们不能使用android:theme属性
                *pre-L,我们通过使用
                *ContextThemeWrapper指向actionBarTheme。
                */
                TypedValue outValue = new TypedValue();
                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

                Context themedContext;
                if (outValue.resourceId != 0) {
                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                } else {
                    themedContext = mContext;
                }

                // 通过themedContext加载视图,并设置为内容视图
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent = (DecorContentParent) subDecor
                        .findViewById(R.id.decor_content_parent);
                mDecorContentParent.setWindowCallback(getWindowCallback());

                /**
                 * 为DecorContentParent设置相应特征
                 */
                if (mOverlayActionBar) {
                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
                }
                if (mFeatureProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
                }
                if (mFeatureIndeterminateProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                }
            }
        } else {//3.有Title时
            //Overlay模式,则加载Overlay模式的视图
            if (mOverlayActionMode) {
                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 (Build.VERSION.SDK_INT >= 21) {
                //运行API版本21及以上,可以依赖ViewCompat的setOnApplyWindowInsetsListener设置监听器
                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                        new OnApplyWindowInsetsListener() {
                            @Override
                            public WindowInsetsCompat onApplyWindowInsets(View v,
                                    WindowInsetsCompat insets) {
                                final int top = insets.getSystemWindowInsetTop();
                                final int newTop = updateStatusGuard(top);

                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(
                                            insets.getSystemWindowInsetLeft(),
                                            newTop,
                                            insets.getSystemWindowInsetRight(),
                                            insets.getSystemWindowInsetBottom());
                                }

                                //应用insets到我们的视图上
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
            } else {
                // Else, we need to use our own FitWindowsViewGroup handling
                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                            @Override
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = updateStatusGuard(insets.top);
                            }
                        });
            }
        }
        //判断是否创建了subDecor
        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);
        }

        // Make the decor optionally fit system windows, like the window's decor
        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
        //这是添加我们布局父容器FrameLayout
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        //获取Window窗口上的视图,android.R.id.content这个Id在以前是我们布局的父容器的Id
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            //4.将在Window已经创建的视图移除后添加到subDecor上
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            //5.标记android.R.id.content视图没有ID
            windowContentView.setId(View.NO_ID);
            //将我们布局父容器FrameLayout的ID设置为android.R.id.content
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        // Now set the Window's content view with the decor
        //6.将我们添加视图的父视图添加到Window上
        mWindow.setContentView(subDecor);

        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}

            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });

        return subDecor;
    }

创建父视图的大概流程如下:

a.初始化视图显示相关特征

b.根据是否有Title加载不同视图

c.将在Window已经创建的子视图移除后添加到subDecor上

d.将我们布局父容器FrameLayout的ID设置为android.R.id.content

e.将我们要添加视图的父视图添加到Window,mWindow.setContentView(subDecor)

我们在创建subDecor时候发现会调用requestWindowFeature(),然后mWindow.getDecorView()创建DecorView添加相应的特征(Feature)设置,大概能了解requestWindowFeature()需要在setContentView()方法之前调用才能起作用,假如在setContentView()之后调用设置无效;例如设置全屏显示:

@Override
protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.MATCH_PARENT
                ,WindowManager.LayoutParams.MATCH_PARENT);

        setContentView(R.layout.activity_main);//android.R.id.content
}

subDecor对应的布局文件abc_screen_simple.xml

xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
 
         http://www.apache.org/licenses/LICENSE-2.0
 
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
 
<android.support.v7.widget.FitWindowsLinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/action_bar_root"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   android:fitsSystemWindows="true">
 
    <android.support.v7.widget.ViewStubCompat
       android:id="@+id/action_mode_bar_stub"
       android:inflatedId="@+id/action_mode_bar"
       android:layout="@layout/abc_action_mode_bar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
 
    <include layout="@layout/abc_screen_content_include" />
 
</android.support.v7.widget.FitWindowsLinearLayout>

abc_screen_content_include.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.internal.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>

subDecor实际是FitWindowsLinearLayout;

依据上面创建视图的过程,分析一下视图结构,实际上FitWindowsLinearLayout外层还有Window层,一会继续分析mWindow.getDecorView()下Window相关的源码;

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

两行的关键代码如下 :

//创建DecorView

mWindow.getDecorView();

//将subDecor添加到Window上
mWindow.setContentView(subDecor);

5)mWindow.getDecorView()

 

Activity
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
}

通过Activity代码会发现Window实际创建是实现类PhoneWindow,Window顶级窗口的抽象类;


public class PhoneWindow extends Window implements MenuBuilder.Callback {}

getDecorView()具体实现

主要负责创建和添加DecorView;

PhoneWindow
@Override
public final View getDecorView() {
    //mDecor为空或者强制DecorView重新安装
    if (mDecor == null || mForceDecorInstall) {
         installDecor();
    }
    return mDecor;
}

installDecor()具体实现

mDecor = generateDecor();

mContentParent = generateLayout(mDecor);

PhoneWindow
private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {//DecorView为空需要新建DecorView
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {//mContentParent为空需要新建mContentParent
            mContentParent = generateLayout(mDecor);//把刚创建的DecorView传进去
            ......
        }
}

generateDecor()具体实现

创建DecorView视图;

protected DecorView generateDecor() {
        ......
        //新建DecorView视图对象
        return new DecorView(getContext(), -1);
    }

DecorView视图实际是帧布局,起到装饰作用;

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    ...
}

generateLayout(mDecor)具体实现

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        //获取窗口的样式Style
        TypedArray a = getWindowStyle();

        if (false) {
            System.out.println("From style:");
            String s = "Attrs:";
            for (int i = 0; i < R.styleable.Window.length; i++) {
                s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
                        + a.getString(i);
            }
            System.out.println(s);
        }
        //设置相关的标志位
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }

        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
            requestFeature(FEATURE_SWIPE_TO_DISMISS);
        }

        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
                false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
                false)) {
            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
        a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
        if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()
                + ", major: " + mMinWidthMajor.coerceToString());
        if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMajor,
                    mFixedWidthMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMinor,
                    mFixedWidthMinor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMajor,
                    mFixedHeightMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMinor,
                    mFixedHeightMinor);
        }
        if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
            requestFeature(FEATURE_CONTENT_TRANSITIONS);
        }
        if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
        }

        mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);

        final Context context = getContext();
        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                R.bool.target_honeycomb_needs_options_menu);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);

        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
        } else {
            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
        }
        //设置主题状态栏默认的颜色
        if (!mForcedStatusBarColor) {
            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
        }
        获取底部NavigationBar颜色
        if (!mForcedNavigationBarColor) {
            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
            mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
                    0x00000000);
        }
        //获取主题的一些资源
        WindowManager.LayoutParams params = getAttributes();

        // Non-floating windows on high end devices must put up decor beneath the system bars and
        // therefore must know about visibility changes of those.
        if (!mIsFloating) {
            if (!targetPreL && a.getBoolean(
                    R.styleable.Window_windowDrawsSystemBarBackgrounds,
                    false)) {
                setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
            }
            if (mDecor.mForceWindowDrawsStatusBarBackground) {
                params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
            }
        }
        if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
        }

        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            if (a.getBoolean(
                    R.styleable.Window_windowCloseOnTouchOutside,
                    false)) {
                setCloseOnTouchOutsideIfNotSet(true);
            }
        }

        if (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(
                    R.styleable.Window_windowSoftInputMode,
                    params.softInputMode);
        }

        if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
                mIsFloating)) {
            /* All dialogs should have the window dimmed */
            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            }
            if (!haveDimAmount()) {
                params.dimAmount = a.getFloat(
                        android.R.styleable.Window_backgroundDimAmount, 0.5f);
            }
        }

        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(
                    R.styleable.Window_windowAnimationStyle, 0);
        }

        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {
                if (mBackgroundResource == 0) {
                    mBackgroundResource = a.getResourceId(
                            R.styleable.Window_windowBackground, 0);
                }
                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }
                mBackgroundFallbackResource = a.getResourceId(
                        R.styleable.Window_windowBackgroundFallback, 0);
                if (false) {
                    System.out.println("Background: "
                            + Integer.toHexString(mBackgroundResource) + " Frame: "
                            + Integer.toHexString(mFrameResource));
                }
            }
            if (mLoadElevation) {
                mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
            }
            mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
            mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
        }

        // 找到Window上DecorView需要显示的布局ID

        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) {
            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啦~
        //将布局添加到DecorView
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        //public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks(contentParent);
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        //这里的getContainer()返回的是个Window类,也就是父Window,一般为空
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            //设置窗口背景
            mDecor.setWindowBackground(background);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if (mTitle != null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

主要流程如下:

a.根据不同的样式找到对应的布局;

例如:layoutResource = R.layout.screen_simple;

b.将布局加载到DecorView上;

c.返回android:id=”@android:id/content”对应的视图contentParent

screen_simple.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

6)Window.setContentView()

将我们的布局ID添加到mContentParent(android:id=”@android:id/content”)视图下

 @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        //调用getDecorView()时已经创建了mContentParent
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //是否有transitions动画,没有,进入else
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //有动画,执行transitions动画
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //重要!!将这个subDecor也就是FitWindowsLinearLayout添加到这个mContentParent里面了
            //mContentParent是FrameLayout,在之前设置的View.NO_ID
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

2.整个添加在Activity中添加视图的流程如下

a.在Activity中调用setContentView();设置我们要显示的视图View或者视图ID

Activity
@Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//android.R.id.content
        ButterKnife.bind(this)
}

b.mWindow = new PhoneWindow(this, window, activityConfigCallback);

加载创建Activity时会执行attach()方法创建Window实现类PhoneWindow对象;

Activity
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
}

c.执行视图加入到Window上

1.mWindow.getDecorView(); 创建DecorView
2.mDecor = generateDecor(-1); 创建DecorView
3.new DecorView(context, featureId, this, getAttributes()); 创建DecorView
4.mContentParent = generateLayout(mDecor); 根据样式找到对应视图
5.layoutResource = R.layout.screen_simple; 根据样式找到对应视图
6.mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 将视图加入到DecorView
7.subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null); 根据样式找到需要的子视图
8.mWindow.setContentView(subDecor); //将需要的子视图Window上mContentParent
9.将我们的视图添加到subDecor上

Activity

  PhoneWindow

    DecorView

        LinearLayout(根据设置特性选择相应的视图装载到DecorView中)

                     ActionBarContextView

                     FrameLayout(“@android:id/content”)设置ID为空

                                    FitWindowsLinearLayout(根据设置特性选择相应的视图装载到FrameLayout(“@android:id/content”))

                                          ActionBarContextView

                                          ContentFrameLayout设置ID(“@android:id/content”)

                                                    将我们要显示的视图加入到 ContentFrameLayout

完整的视图结构如下:

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

运行APP后,我们在用hierarchyviewer查看下

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上
 

3.学习总结

在Activity调用setContentView()时,加载Activity系统会先创建Window,Window里创建DecorView,将根据设置的样式选择底层的视图添加到DecorView,然后根据样式找到子Decor(FitWindowsLinearLayout),加入到DecorView下的视图(FrameLayout),最后将我们自己视图添加到ContentFrameLayout;

setContentView调用时序图:

AppCompatActivity.setContentView如何装载视图到AppCompatActivity上

                                                   

  

参考:

https://blog.csdn.net/yewei02538/article/details/60979075

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153257.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月26日 上午11:36
下一篇 2022年6月26日 上午11:36


相关推荐

  • canvas – 绘制图片,图片变模糊问题解决)

    canvas – 绘制图片,图片变模糊问题解决)问题 canvas 绘制图片 图片变模糊设定一个一定尺寸的 canvas 我这里设置的画布大小是 400px 400px 当一张图片完全画到画布上的时候 大概率都会出现图片模糊的情况 我拿下面一张图片画到 canvas 上作为例子 看上去应该比较明显的有模糊的感觉 单方面的去修改图片精度 换成更高清的图片 事实证明确实有一丢丢用 但是效果不是很明显 况且我当时那个图片由于是手绘的 大小有 5M 也不会切片加载图片 直接整个加载非常耗时 那么如何去处理这样的问题呢 生活中应该大家都遇到过这样的情况

    2026年3月18日
    1
  • hibernate二级缓存(二)二级缓存实现原理简单剖析

    hibernate二级缓存(二)二级缓存实现原理简单剖析hibernate二级缓存(一)二级缓存的实现原理在前面我们将过hibernate二级缓存类似于一个插件,将缓存的具体实现分离,缓存的具体实现是通过hibernate.cache.region.factory_class参数配置指定。1.hibernate二级缓存结构hibernate二级缓存涉及到如下几个重要的接口:RegionFactoryDomainDataRegionEnt…

    2022年5月24日
    46
  • win7显示器亮度怎么调_虚拟机外接显示器

    win7显示器亮度怎么调_虚拟机外接显示器很久没有继续研究wddmhook了,最后一次研究还在3年前,不得不说虽然应用的少,但是wddmhook却是很有技术含量的一项技术,而且实用性很高,我们除了做虚拟显示器还能做很多的东西,比如高效的截屏(因为直接从驱动层面截屏,所以效率和实现效果秒杀mirrordriver和DXGI)、视频重定向(视频硬件加速的时候可以直接获取原始码流)、修改原始的显示器厂商信息、修改显示器支持分辨率,等等骚操作wddmhook都可以实现。去年,深圳某硬件公司找到我,他们正在做usb转HDMI或者USB转VGA的硬件

    2022年8月21日
    10
  • 斩获 offer 的 Java 面试宝典

    斩获 offer 的 Java 面试宝典这是本人整理的数万字的面试笔记 基本上涵盖了 Java 领域的所有技术栈 本人也是凭借这份面试笔记斩获了近 10 个 offer 面试成功率高达 80 当然这份笔记是我根据自身的经验和技术栈整理的 自己觉得很重要的或者记不清的就会记录记录下来 面试被问到的时候也有回答的思路 现在共享给大家 希望对准备面试的小伙伴有帮助 1 微服务 1 1 主流注册中心对比 zookeeper zookeeper 作为注册中心主要是因为它具有节点变更通知功能 只要客户端监听相关服务节点 服务节点有所变更就能及时的通知到监

    2026年3月19日
    3
  • oracle存储过程回滚_sql事务回滚

    oracle存储过程回滚_sql事务回滚创建表createtableCourse (NoINT,Namenvarchar(10),Commentnvarchar(10))插入数据insertintocoursevalues(‘332′,’语言在’,’234′)insertintocoursevalues(‘332′,’语言在’,’234′)创建存储过程ALTER

    2022年10月21日
    5
  • Linux的777权限

    Linux的777权限Linux 的每个文件一般都有三个权限 r 读 w 写 x 执行 其分别对应的数值为 4 2 1 输入 ll 可以查看到文件的权限 聪明的同学已经看出来了 7 即代表有读 写和执行的权限 那么为什么是三个 7 呢 三 是指三种不同的对象 User Gourp Other 即文件所有人 文件所有组 和其他人 Linux 修改 we 文件权限的命令为 chmodxxxfile

    2026年3月26日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号