Android P窗口机制之DecorView加载流程

本文主要介绍app的View加载过程和StatusBar,NavigationBar的view的加载过程。

总体上来讲:一个用户看的见的UI布局为,状态栏View,app程序的UI界面ContentView,导航栏View。

相关类:
frameworks\base\core\java\android\view\View.java
frameworks\base\core\java\android\app\Activity.java
frameworks\base\core\java\com\android\internal\policy\DecorView.java
frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
frameworks\base\core\res\res\values\attrs.xml

关键的实现在DecorView和PhoneWindow中。

1、app执行setContentView

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

2、Activity中的setContentView

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

public Window getWindow() {
    return mWindow;
}

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);

		//省略一部分代码
		//...
}

2、PhoneWindow中的setContentView
第一步:installDecor(),创建顶层窗口DecorView

@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.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            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 = generateLayout(mDecor);
	
			//省略一部分代码、
			//...
		}
		
		//省略一部分代码、
		//...
}

//通过new DecorView()创建一个顶层窗口DecorView
protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, getContext());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

DecorView创建

上述流程可以看到DecorView是在generateDecor中创建的。创建完成后,会在installDecor中执行generateLayout(),

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

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

		//省略一部分代码、
		//...

        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            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!");
        }

		//关键点1
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        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.
        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;
}
加载窗口式样

注意generateLayout这个函数,做了很多工作,根据主题式样getWindowStyle(),去设置DecorView的风格,常见的窗口式样有:

<!-- The set of attributes that describe a Windows's theme. -->
<declare-styleable name="Window">
    <attr name="windowBackground" />
    <attr name="windowBackgroundFallback" />
    <attr name="windowContentOverlay" />
    <attr name="windowFrame" />
    <attr name="windowNoTitle" />
    <attr name="windowFullscreen" />
    <attr name="windowOverscan" />
    <attr name="windowIsFloating" />
    <attr name="windowIsTranslucent" />
    <attr name="windowShowWallpaper" />
    <attr name="windowAnimationStyle" />
    <attr name="windowSoftInputMode" />
    <attr name="windowDisablePreview" />
    <attr name="windowNoDisplay" />
    <attr name="textColor" />
    <attr name="backgroundDimEnabled" />
    <attr name="backgroundDimAmount" />
    <attr name="windowActionBar" />
    <attr name="windowActionModeOverlay" />
    <attr name="windowActionBarOverlay" />
    <attr name="windowEnableSplitTouch" />
    <attr name="windowCloseOnTouchOutside" />
    <attr name="windowTranslucentStatus" />
    <attr name="windowTranslucentNavigation" />
    <attr name="windowSwipeToDismiss" />
    <attr name="windowContentTransitions" />
    <attr name="windowActivityTransitions" />
    <attr name="windowContentTransitionManager" />
    <attr name="windowActionBarFullscreenDecorLayout" />
	//省略一部分
	//...
</declare-styleable>

详细说明参照官网:https://developer.android.com/reference/android/R.attr

app中的用法列举,在styles.xml中增加:

<!-- Base application theme. -->
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
</style>

DecorView的布局可参考文章:https://blog.csdn.net/a553181867/article/details/51477040

在这里插入图片描述
关键点1、 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
根据查看layoutResource的赋值,可以看出是根据主题式样来确定的layout。主要分为以下几个:

  • R.layout.screen_swipe_dismiss; //主题式样设置true,Activity向右侧滑可以退出
  • R.layout.screen_title_icons;
    onCreate中设置如下代码:
	@Override
	protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    supportRequestWindowFeature(Window.FEATURE_LEFT_ICON);
	    setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,R.drawable.ic_launcher_background);
	    setContentView(R.layout.activity_main);
	}
	requestWindowFeature可以设置的值有:
	1.DEFAULT_FEATURES:系统默认状态,一般不需要指定
	2.FEATURE_CONTEXT_MENU:启用ContextMenu,默认该项已启用,一般无需指定
	3.FEATURE_CUSTOM_TITLE:自定义标题。当需要自定义标题时必须指定。如:标题是一个按钮时
	4.FEATURE_INDETERMINATE_PROGRESS:不确定的进度
	5.FEATURE_LEFT_ICON:标题栏左侧的图标
	6.FEATURE_NO_TITLE:无标题
	7.FEATURE_OPTIONS_PANEL:启用“选项面板”功能,默认已启用。
	8.FEATURE_PROGRESS:进度指示器功能
	9.FEATURE_RIGHT_ICON:标题栏右侧的图标
	详细说明可以参考:https://blog.csdn.net/axi295309066/article/details/53363792
  • R.layout.screen_progress; //设置FEATURE_PROGRESS窗口特性并且 false
  • R.layout.screen_custom_title;
  • R.layout.screen_action_bar
  • R.layout.screen_title;
  • R.layout.screen_simple_overlay_action_mode;
  • R.layout.screen_simple; //这是屏幕的优化布局,启用了最少的功能特性

不一一说明了。
onResourcesLoaded将上面layout布局添加到DecorVew中。

关键点2、ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
创建了一个contentParent View,用来加载app的activity_main.xml,见上面的结构图,就是结构图中的ContentView。将返回的contentParent赋值给mContentParent。

再回头看setContentView中的实现:
mLayoutInflater.inflate(layoutResID, mContentParent);
或者:
mContentParent.addView(view, params);
这里就是加载app的activity_main.xml或者add其他的layout view。并且设置mContentParent为activity_main.xml的父布局。

加载状态栏Status Bar

mContentParent的布局, ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
ID_ANDROID_CONTENT就是DecorView的ContentView,用来加载app的activity_main.xml。

官网解释:https://developer.android.com/reference/android/view/Window

The ID that the main layout in the XML layout file should have.

其实就是加载之前layout中的的FrameLayout android:id="@android:id/content"。

列举一个R.layout.screen_simple的代码:

<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>

关键点是: android:fitsSystemWindows=“true”

回调执行DecorView中的onApplyWindowInsets():

@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    final WindowManager.LayoutParams attrs = mWindow.getAttributes();
    mFloatingInsets.setEmpty();

	Log.d(TAG, "sunxiaolin,onApplyWindowInsets(0x" + Integer.toHexString(attrs.flags) + ")");

    if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
        // For dialog windows we want to make sure they don't go over the status bar or nav bar.
        // We consume the system insets and we will reuse them later during the measure phase.
        // We allow the app to ignore this and handle insets itself by using
        // FLAG_LAYOUT_IN_SCREEN.
        if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
            mFloatingInsets.top = insets.getSystemWindowInsetTop();
            mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
            insets = insets.inset(0, insets.getSystemWindowInsetTop(),
                    0, insets.getSystemWindowInsetBottom());
        }
        if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
            mFloatingInsets.left = insets.getSystemWindowInsetTop();
            mFloatingInsets.right = insets.getSystemWindowInsetBottom();
            insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
                    insets.getSystemWindowInsetRight(), 0);
        }
    }
    mFrameOffsets.set(insets.getSystemWindowInsets());
    //更新Status Bar颜色或者View
    insets = updateColorViews(insets, true /* animate */);
    insets = updateStatusGuard(insets);
    if (getForeground() != null) {
        drawableChanged();
    }
    return insets;
}

关键点:updateColorViews();

源码:

WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
    WindowManager.LayoutParams attrs = mWindow.getAttributes();
    int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();

    // IME is an exceptional floating window that requires color view.
    final boolean isImeWindow =
            mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;

    if (!mWindow.mIsFloating || isImeWindow) {
        boolean disallowAnimate = !isLaidOut();
        disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
                & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
        mLastWindowFlags = attrs.flags;

        if (insets != null) {
            mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
                    insets.getSystemWindowInsetTop());
            mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
                    insets.getSystemWindowInsetBottom());
            mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
                    insets.getSystemWindowInsetRight());
            mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
                    insets.getSystemWindowInsetLeft());

            // Don't animate if the presence of stable insets has changed, because that
            // indicates that the window was either just added and received them for the
            // first time, or the window size or position has changed.
            boolean hasTopStableInset = insets.getStableInsetTop() != 0;
            disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
            mLastHasTopStableInset = hasTopStableInset;

            boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
            disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
            mLastHasBottomStableInset = hasBottomStableInset;

            boolean hasRightStableInset = insets.getStableInsetRight() != 0;
            disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
            mLastHasRightStableInset = hasRightStableInset;

            boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
            disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
            mLastHasLeftStableInset = hasLeftStableInset;

            mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
        }

        boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
        boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
        int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
        updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
                mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
                navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                0 /* sideInset */, animate && !disallowAnimate, false /* force */);

        boolean statusBarNeedsRightInset = navBarToRightEdge
                && mNavigationColorViewState.present;
        boolean statusBarNeedsLeftInset = navBarToLeftEdge
                && mNavigationColorViewState.present;
        int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                : statusBarNeedsLeftInset ? mLastLeftInset : 0;
        updateColorViewInt(mStatusColorViewState, sysUiVisibility,
                calculateStatusBarColor(), 0, mLastTopInset,
                false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
                animate && !disallowAnimate,
                mForceWindowDrawsStatusBarBackground);
    }

    // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
    // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
    // explicitly asked for it.
    // 判断是否显示导航栏,即ContentView显示区域是否延伸到NavigationBar下方
    boolean consumingNavBar =
            (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                    && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
                    && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
            || mLastShouldAlwaysConsumeNavBar;

    // If we didn't request fullscreen layout, but we still got it because of the
    // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
    // 没有请求全屏,计算ContentView是否延伸到StatuaBar下方显示,即根据StatusBar告诉计算ContentView显示区域
    boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
            && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
            && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
            && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
            && mForceWindowDrawsStatusBarBackground
            && mLastTopInset != 0;

    int consumedTop = consumingStatusBar ? mLastTopInset : 0;
    int consumedRight = consumingNavBar ? mLastRightInset : 0;
    int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
    int consumedLeft = consumingNavBar ? mLastLeftInset : 0;

//根据上面的consumingStatusBar 和 consumingNavBar 来计算ContentView的显示区域。
    if (mContentRoot != null
            && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
        MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
        if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
                || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
            lp.topMargin = consumedTop;
            lp.rightMargin = consumedRight;
            lp.bottomMargin = consumedBottom;
            lp.leftMargin = consumedLeft;
            mContentRoot.setLayoutParams(lp);
			//重新计算结果
            if (insets == null) {
                // The insets have changed, but we're not currently in the process
                // of dispatching them.
                requestApplyInsets();
            }
        }
        if (insets != null) {
            insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
        }
    }

    if (insets != null) {
        insets = insets.consumeStableInsets();
    }
    return insets;
}

consumedTop 的条件:
1、sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,没有设置SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN属性
2、sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,没有设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,该窗口透明,显示SystemUI中StatusBar的颜色
3、attrs.flags & FLAG_LAYOUT_IN_SCREEN,没有设置FLAG_LAYOUT_IN_SCREEN属性,该属性忽略周围的装饰View(例如状态栏)
4、attrs.flags & FLAG_LAYOUT_INSET_DECOR,没有设置FLAG_LAYOUT_INSET_DECOR属性,与FLAG_LAYOUT_IN_SCREEN结合使用,效果类似,确保StatusBar不会挡住你的UI。
5、mForceWindowDrawsStatusBarBackground,背景透明
6、mLastTopInset != 0 上一次Top消费不为0,

关键点:updateColorViewInt();
updateColorViews中执行了两次updateColorViewInt()方法,第一次传入的参数是mNavigationColorViewState,第二次传入的参数是mStatusColorViewState

DecorView创建Status Bar和Navigation Bar的View,都是在updateColorViewInt中完成的。这些View只是一个带背景色或者透明的View,不做任何其他的UI显示,真正的Status Bar和Navigation Bar的UI是在SystemUI中完成的,通过window窗口实现。

源码:

private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
        int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
        boolean animate, boolean force) {
    state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
    boolean show = state.attributes.isVisible(state.present, color,
            mWindow.getAttributes().flags, force);
    boolean showView = show && !isResizing() && size > 0;

    boolean visibilityChanged = false;
    View view = state.view;

    int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
    int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
    int resolvedGravity = verticalBar
            ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
            : state.attributes.verticalGravity;

//add by sunxiaolin
    if (view == null) {
        if (showView) {
			//当view为null时,穿件View,其实就是创建Status Bar和Navigation Bar的View
            state.view = view = new View(mContext);
            setColor(view, color, dividerColor, verticalBar, seascape);
            view.setTransitionName(state.attributes.transitionName);
            view.setId(state.attributes.id);
            visibilityChanged = true;
            view.setVisibility(INVISIBLE);
            state.targetVisibility = VISIBLE;

            LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                    resolvedGravity);
            if (seascape) {
                lp.leftMargin = sideMargin;
            } else {
                lp.rightMargin = sideMargin;
            }
            addView(view, lp);
            updateColorViewTranslations();
        }
    } else {
        int vis = showView ? VISIBLE : INVISIBLE;
        visibilityChanged = state.targetVisibility != vis;
        state.targetVisibility = vis;
        LayoutParams lp = (LayoutParams) view.getLayoutParams();
        int rightMargin = seascape ? 0 : sideMargin;
        int leftMargin = seascape ? sideMargin : 0;
        if (lp.height != resolvedHeight || lp.width != resolvedWidth
                || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
                || lp.leftMargin != leftMargin) {
            lp.height = resolvedHeight;
            lp.width = resolvedWidth;
            lp.gravity = resolvedGravity;
            lp.rightMargin = rightMargin;
            lp.leftMargin = leftMargin;
            view.setLayoutParams(lp);
        }
        if (showView) {
            setColor(view, color, dividerColor, verticalBar, seascape);
        }
    }
    if (visibilityChanged) {
        view.animate().cancel();
        if (animate && !isResizing()) {
            if (showView) {
                if (view.getVisibility() != VISIBLE) {
                    view.setVisibility(VISIBLE);
                    view.setAlpha(0.0f);
                }
                view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
                        setDuration(mBarEnterExitDuration);
            } else {
                view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
                        .setDuration(mBarEnterExitDuration)
                        .withEndAction(new Runnable() {
                            @Override
                            public void run() {
                                state.view.setAlpha(1.0f);
                                state.view.setVisibility(INVISIBLE);
                            }
                        });
            }
        } else {
            view.setAlpha(1.0f);
            view.setVisibility(showView ? VISIBLE : INVISIBLE);
        }
    }
    state.visible = show;
    state.color = color;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaolin2016

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值