AppCompatActivity.setContentView(与activity.setContentView区别)方法解读

news2024/9/21 10:27:24

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

1.setContentView()调用流程

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

1.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通过代理模式添加要显示的视图;

然后看看这个里面 setContentView 是从哪里来的?这是 AppCompatDelegate 这个的一个函数,可以看看这个类是做什么用的

 This class represents a delegate which you can use to extend AppCompat's support to any
 * {@link android.app.Activity}.

翻译过来就是: 此类表示可用于将AppCompat支持扩展到任何Activity的委托。
AppCompatDelegate是抽像类,具体的实现是AppCompatDelegateImp。

1.2 AppCompatDelegateImp$setContentView

     @Override
    public void setContentView(int resId) {
        //初始化DecorView
        ensureSubDecor();
        //设置setContentView 要记载的父布局
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        //加载之前移除所有的布局
        contentParent.removeAllViews();
        //完成布局的加载
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }

1.3 ensureSubDecor();

 private void ensureSubDecor() {
            //默认是false,如果为true的表明 subDecor已经被加载过了
        if (!mSubDecorInstalled) {
        
            mSubDecor = createSubDecor();


            //设置actiivty 的title
            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (mDecorContentParent != null) {
                    mDecorContentParent.setWindowTitle(title);
                } else if (peekSupportActionBar() != null) {
                    peekSupportActionBar().setWindowTitle(title);
                } else if (mTitleView != null) {
                    mTitleView.setText(title);
                }
            }

            //为subDecor 设置固定的大小
            applyFixedSizeWindow();

            //空方法
            onSubDecorInstalled(mSubDecor);

            //设置 subDecor 已经被加载过了
            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 (!mIsDestroyed && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

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

1.3.1 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()之后调用设置无效;

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层
在这里插入图片描述
createSubDecor方法中还有两行的关键代码如下 :
//创建DecorView
mWindow.getDecorView();

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

1.3.2 mWindow.getDecorView()

getDecorView()具体实现

主要负责创建和添加DecorView;

PhoneWindow.java
@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>

1.3.3 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;
    }

整个setContentView的流程如下:


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/")设置ID为空
                                    FitWindowsLinearLayout(根据设置特性选择相应的视图装载到FrameLayout("@android:id/content"))
                                          ActionBarContextView
                                          ContentFrameLayout设置ID("@android:id/content")
                                                    将我们要显示的视图加入到 ContentFrameLayout

完整的视图结构如下:
在这里插入图片描述
运行APP后,我们在用hierarchyviewer查看下
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络原理】 (3) (网络层 IP协议 地址管理 路由选择 数据链路层 以太网 MTU 补充:DNS)

文章目录 网络层IP协议地址管理路由选择 数据链路层以太网MTU补充:DNS 网络层 IP协议 网络层的代表,IP协议. 4位版本号&#xff08;version&#xff09;&#xff1a;指定IP协议的版本&#xff0c;对于IPv4来说&#xff0c;就是4。4位头部长度&#xff08;header length&…

98. Python基础教程:try...except...finally语句

【目录】 文章目录 1. try...except...finally语法介绍2. try...except...finally执行顺序3. 捕获特定类型的异常4. 捕获所有类型的异常5. 实操练习-打开txt文件并输出文件内容 【正文】 在今天的课程中&#xff0c;我们将学习Python中的异常处理语句try...except...finally。 …

excel英语翻译让你的数据更容易被理解

从前有一个名叫小明的办公室职员&#xff0c;他每天都要处理大量的数据和报表。然而&#xff0c;由于工作需要&#xff0c;他经常收到来自不同国家的Excel表格&#xff0c;这些表格上的内容都是用各种各样的语言编写的&#xff0c;让他很难理解其中的意思。这时&#xff0c;小明…

Qt Creator 11 开放源码集成开发环境新增集成终端和 GitHub Copilot 支持

导读Qt 项目今天发布了 Qt Creator 11&#xff0c;这是一款开源、免费、跨平台 IDE&#xff08;集成开发环境&#xff09;软件的最新稳定版本&#xff0c;适用于 GNU/Linux、macOS 和 Windows 平台。 Qt Creator 11 的亮点包括支持标签、多外壳、颜色和字体的集成终端模拟器&am…

建模教程:如何利用3ds Max 和 After Effects 实现多通道渲染和后期合成

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建基本场景 步骤 1 打开 3ds Max。在 透视视口。 打开 3ds Max 步骤 2 做一个茶壶&#xff0c;放在飞机上。 制作茶壶 步骤 3 我在场景中应用了几个灯光。我选择了光线追踪阴影作为阴影。 光线追…

走进人工智能| 智能物联网 AIoT的魅力交织

前言&#xff1a; AIIoT是指人工智能&#xff08;AI&#xff09;与物联网&#xff08;IoT&#xff09;的结合。智能物联网是一种技术体系&#xff0c;通过连接和集成物理设备、传感器和互联网&#xff0c;实现设备之间的智能交互和数据共享&#xff0c;为人们提供智能化、自动化…

赛车游戏——【极品飞车】(内含源码inscode在线运行)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

IEEE 802.11——无线局域网的重要里程碑

概要 无线局域网&#xff08;Wireless Local Area Network&#xff0c;WLAN&#xff09;已经成为现代生活中不可或缺的一部分&#xff0c;它为我们提供了便捷的无线网络连接&#xff0c;让我们能够在家中、办公室、公共场所等地轻松上网。在无线局域网技术的发展过程中&#x…

【C++】模板进阶(模板的特化,非类型模板参数,模板的分离编译)

文章目录 一、模板使用时一定要加typename的情况二、 非类型模板参数三、模板的特化1.函数模板特化2.类模板特化1.全特化&#xff1a;2. 偏特化&#xff1a;1. 部分特化2.参数更一步限制 四、模板的分离编译1.Stack.h2.Stack.cpp(定义)3.test.cpp 一、模板使用时一定要加typena…

【taro react】---- 获取元素的位置和宽高等信息

1. 需求分析 添加节点的布局位置的查询请求。相对于显示区域&#xff0c;以像素为单位。其功能类似于 DOM 的 getBoundingClientRect。返回 NodesRef 对应的 SelectorQuery。区分小程序和H5的环境&#xff0c;调用 getBoundingClientRect 获取对应的信息。 2. H5 实现 判断传…

根据前序和中序遍历序列构造二叉树 (递归+迭代两种方法实现)

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]源代码如下…

113、单例Bean是单例模式吗?

单例Bean是单例模式吗? 通常来说,单例模式是指在一个JVM中,一个类只能构造出来一个对象,有很多方法来实现单例模式,比如懒汉模式,但是我们通常讲的单例模式有一个前提条件就是规定在一个JVM中,那如果要在两个JVM中保证单例呢?那可能就要用分布式锁这些技术,这里的重点…

wangzherongyao yase

17132血量出装&#xff0c;60%物理抗性&#xff0c;57%法扛的亚瑟&#xff0c;打野刀爆裂提高移动#王者荣耀#亚瑟&#xff0c;后期红莲烧664左右打野刀烧254左右&#xff0c;被动天赋回血也非常快 亚瑟打这些纯输出上单跟砍瓜切菜一样

软考 系统分析师和系统架构师 项目管理师

软考整起 https://www.ruankao.org.cn/ 什么是计算机技术与软件&#xff08;初级、中级、高级&#xff09;考试&#xff08;软考&#xff09;&#xff1f; - 知乎 系统分析师和系统架构师关系 这两年&#xff0c;我先后报考了计算机技术与软件专业技术资格&#xff08;水平&a…

基于ARM+FPGA的驱控一体机器人控制器设计

目前市场上工业机器人&#xff0c;数控机床等多轴运动控制系统普遍采用运动控制器加 伺服驱动器的分布式控制方式。在这种控制方式中&#xff0c;控制器一方面完成人机交互&#xff0c;另 一方面进行 NC 代码的解释执行&#xff0c;插补运算&#xff0c;继而将计算出来的位…

rsync远程同步+inotify实时同步部署

文章目录 一、rsync简介1.rsync同步方式2、备份的方式3.rsync同步源 二、rsync命令基本用法三、配置源的两种表达方法四、配置服务端与客户端的实验1.将 Master 服务器数据备份到 Slave 服务器2.配置RsyncInotify 实时同步 五、rsync的应用场景总结 一、rsync简介 Rsync&#…

MySQL 储存过程

前言 存储过程&#xff08;Stored Procedure&#xff09;是一组预定义的SQL语句集合&#xff0c;它们被保存在数据库中并可供重复调用。存储过程可以接受参数、执行查询和更新操作&#xff0c;并返回结果。使用存储过程&#xff0c;可以实现复杂的业务逻辑和数据操作&#xff0…

浅谈微服务异步解决方案

导言 异步是一种设计思想&#xff0c;不是设计目的&#xff0c;因此不要为了异步而异步&#xff0c;要有所为&#xff0c;有所不为。 异步不是『银弹』&#xff0c; 避免试图套用一个『异步框架』解决所有问题&#xff0c; 需要根据不同的业务特点或要求&#xff0c;选择合适的…

助力青少年科技创新人才培养,猿辅导投资1亿元设立新基金

近日&#xff0c;在日本千叶县举办的2023年第64届国际数学奥林匹克&#xff08;IMO&#xff09;竞赛公布比赛结果&#xff0c;中国队连续5年获得团体第一。奖牌榜显示&#xff0c;代表中国参赛的6名队员全部获得金牌。其中&#xff0c;猿辅导学员王淳稷、孙启傲分别以42分、39分…

Ubuntu 离线部署的常见操作

Ubuntu 离线安装的常见操作 **说明&#xff1a;**很多情况下,生产环境都是离线环境&#xff0c;然而开发环境都是互联网的环境&#xff0c;因此部署的过程中需要构建离线安装包; 1. 下载但是不安装 # 例如使用 apt 下载 wireshark 安装包 sudo apt download wireshark # 下载…