全屏、沉浸式、fitSystemWindow使用及原理分析:全方位控制“沉浸式”的实现

本文深入探讨Android中全屏和沉浸式界面的实现,通过分析fitSystemWindow属性、WindowInsets消费、状态栏和导航栏颜色更新原理,揭示了如何控制内容区域扩展到系统UI下方,实现全屏和沉浸式体验。讨论了DecorView、SystemUi以及不同层级的fitSystemWindow消费,并提供了相关配置与实践案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

状体栏颜色设置原理与导航栏颜色设置原理
fitSystemWindow全屏及WindowInsets消费原
fitSystemWindow与padding不同层级的消费
Theme中window属性配置影响
SystemUi及状体栏添加原理

前言

状态栏与导航栏属于SystemUi的管理范畴,虽然界面的UI会受到SystemUi的影响,但是,APP并没有直接绘制SystemUI的权限与必要。APP端之所以能够更改状态栏的颜色、导航栏的颜色,其实还是操作自己的View更改UI。可以这么理解:状态栏与导航栏拥有自己独立的窗口,而且这两个窗口的优先级较高,会悬浮在所有窗口之上,可以把系统自身的状态栏与导航栏看做全透明的,之所有会有背景颜色,是因为下层显示界面在被覆盖的区域添加了颜色,之后,通过SurfaceFlinger的图层混合,好像是状态栏、导航栏自身有了背景色。看一下一个普通的Activity展示的时候,所对应的Surface(或者说Window也可以)。

image

  • 第一个XXXXActivity,大小是屏幕大小
  • 第二个状态栏StatusBar,大小对应顶部那一条
  • 第三个是底部虚拟导航栏NavigationBar,大小对应底部那一条
  • HWC_FRAMEBUFFER_TARGET:是合成的目标Layer,不参与合成

从上表可以看出,虽然只展示了一个Activity,但是同时会有StatusBar、NavigationBar、XXXXActivity可以看出Activity是在状态栏与导航栏下面的,被覆盖了,它们共同参与显示界面的合成,但是,StatusBar、NavigationBar明显不是属于APP自身UI管理的范畴。下面就来分析一下,APP层的API如何影响SystemUI的显示的,并一步步解开所谓沉浸式与全屏的原理,首先看一下如何更改状态栏颜色。

状态栏颜色更新原理

假设当前的场景是默认样式的Activity,如果想要更新状态栏颜色只需要如下代码:

getWindow().setStatusBarColor(RED);

其实这里调用的是PhoneWindow的setStatusBarColor函数,无论是Activity还是Dialog都是被抽象成PhoneWindow:

@Override
public void setStatusBarColor(int color) {
   
    mStatusBarColor = color;
    mForcedStatusBarColor = true;
    if (mDecor != null) {
   
        mDecor.updateColorViews(null, false /* animate */);
    }
}

最终调用的是DecorView的updateColorViews函数,DecorView是属于Activity的PhoneWindow的内部对象,也就说,更新的对象从所谓的Window进入到了Activity自身的布局视图中,接着看DecorView,这里只关注更改颜色:

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

        if (!mIsFloating && ActivityManager.isHighEndGfx()) {
   
            boolean disallowAnimate = !isLaidOut();
            disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
                    & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
            mLastWindowFlags = attrs.flags;
            ...
            boolean statusBarNeedsRightInset = navBarToRightEdge
                    && mNavigationColorViewState.present;
            int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
            <!--更新Color-->
            updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
                    mLastTopInset, false /* matchVertical */, statusBarRightInset,
                    animate && !disallowAnimate);
        }
        ...
    }

这里mStatusColorViewState其实就代表StatusBar的背景颜色对象,主要属性包括显示的条件以及颜色值:

    private final ColorViewState mStatusColorViewState = new ColorViewState(
            SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
            Gravity.TOP,
            Gravity.LEFT,
            STATUS_BAR_BACKGROUND_TRANSITION_NAME,
            com.android.internal.R.id.statusBarBackground,
            FLAG_FULLSCREEN);

如果当前对应Window的SystemUi设置了SYSTEM_UI_FLAG_FULLSCREEN后,就会隐藏状态栏,那就不在需要为状态栏设置背景,否则就设置:

  private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
                int size, boolean verticalBar, int rightMargin, boolean animate) {
   
                <!--关键点1 条件1-->
            state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
                    && (getAttributes().flags & state.hideWindowFlag) == 0
                    && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
               <!--关键点2 条件2-->
            boolean show = state.present
                    && (color & Color.BLACK) != 0
                    && (getAttributes().flags & state.translucentFlag) == 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 ? state.horizontalGravity : state.verticalGravity;

            if (view == null) {
   
                if (show) {
   
                    state.view = view = new View(mContext);
                    view.setBackgroundColor(color);
                    view.setTransitionName(state.transitionName);
                    view.setId(state.id);
                    visibilityChanged = true;
                    view.setVisibility(INVISIBLE);
                    state.targetVisibility = VISIBLE;
            <!--关键点3-->
                    LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                            resolvedGravity);
                    lp.rightMargin = rightMargin;
                    addView(view, lp)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值