文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【UI篇】Android 沉浸式状态栏的那些事

2023-08-17 07:19

关注

此篇文章介绍的内容都是考虑到5.0 版本以上。5.0以下的不做介绍。演示的代码皆为kotlin实现
首先一般设置是包括两方面的,第一是设置 window.decorView.systemUiVisibility ,第二是设置 window.addFlags() 这些方法都有一些常量的设置。但是在API 30 版本之后又新出了一个 WindowInsetsControllerCompat 的类,用来提供一个统一的一些操作。

参考: 传送门
能够造成SystemUI Flag被系统自动清除的交互分类

1.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:(>=api16)

作用是隐藏系统NavigationBar。
但是用户的任何交互,都会导致此Flag被系统清除,进而导航栏自动重新显示,同时View.SYSTEM_UI_FLAG_FULLSCREEN也会被自动清除,因此StatusBar也会同时显示出来。

2.View.SYSTEM_UI_FLAG_FULLSCREEN:(>=api16)

作用是隐藏StatusBar。
和WindowManager.LayoutParams.FLAG_FULLSCREEN有相同视觉效果。不同在于,此Flag一般用在暂时需要全屏的情形(如:阅读应用,全屏视频等),以便让用户的注意力暂时集中在内容上,而如果只是简单的需要一直停留在全屏状态(如:游戏应用),使用WindowManager.LayoutParams.FLAG_FULLSCREEN则是更好的选择。
此Flag会因为各种的交互(如:跳转到其他应用,下拉StatusBar,弹出键盘)的发送而被系统清除。

3.View.SYSTEM_UI_FLAG_IMMERSIVE:(>=api19)

作用:避免某些用户交互造成系统自动清除全屏状态。

4.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:(>=api19)

作用:避免某些用户交互造成系统自动清除全屏状态。同时Activity的部分内容也因此被StatusBar覆盖遮挡。

5.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:(>=api16)

作用:在不隐藏导航栏的情况下,将Activity的显示范围扩展到导航栏底部。同时Activity的部分内容也因此被NavigationBar覆盖遮挡。

6.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:(>=api16)

作用:在不隐藏StatusBar的情况下,将view所在window的显示范围扩展到StatusBar下面。同时Activity的部分内容也因此被StatusBar覆盖遮挡。

7.View.SYSTEM_UI_FLAG_LAYOUT_STABLE:

作用: 稳定布局。当StatusBar和NavigationBar动态显示和隐藏时,系统为fitSystemWindow=true的view设置的padding大小都不会变化,所以view的内容的位置也不会发生移动。

1.WindowMananger.FLAG_TRANSLUCENT_STATUS: (>=api16)

2.WindowMananger.FLAG_FULLSCREEN:

3.WindowMananger.FLAG_TRANSLUCENT_NAVIGATION:

4.WindowMananger.FLAG_DRAWS_SYSTEM_BAR_BACKGROUND:

上面的参数常量,大部分都会提示已经过期了,因为在API 30 版本里面出了WindowInsetsControllerCompat 的一个新类。
比如 View.SYSTEM_UI_FLAG_FULLSCREEN 以及 WindowManager.LayoutParams.FLAG_FULLSCREEN 常量点进去都会提示使用:

但是以上两个常量的作用并不一样。 View.SYSTEM_UI_FLAG_FULLSCREEN 的隐藏作用在下拉状态栏之后就会取消隐藏。需要搭配 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 来位置隐藏的效果。 而查看 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 源码又会提示使用:

经过验证也得出 WindowInsetsController#hide(int) 方法的隐藏会被下拉状态栏操作取消。需要设置 controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 来维持隐藏的状态。

隐藏状态栏(全屏显示)

隐藏状态栏分两种情况,一个是在非刘海屏或挖孔屏,也就是正常的直面屏上。

true
//第二种window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
//第三种window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_FULLSCREEN
WindowInsetsControllerCompat(window, window.decorView).let {     it.hide(WindowInsetsCompat.Type.navigationBars())     it.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}

WindowInsetsController方式实现:

WindowInsetsControllerCompat(window, window.decorView).let {            it.hide(WindowInsetsCompat.Type.navigationBars())            it.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE        }

在这里插入图片描述

在这里插入图片描述
可以看到上面有一条很粗的黑边。因为那是前摄挖孔所在,默认显示内容是不能占用挖孔的地方的。
而google 官方是有针对刘海屏的适配方案的,简单的说就是可以让显示内容上移到刘海状态栏的位置。有三种方式供选择:

 //默认情况,全屏页面不可用刘海区域,非全屏页面可以进行使用 public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; //不允许使用刘海区域 public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2; //允许页面延伸到刘海区域 public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;

使用方式如下:

if (Build.VERSION.SDK_INT >= 28) {            val lp = window.attributes            lp.layoutInDisplayCutoutMode =                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES            window.attributes = lp        }

但是加上这个只是允许window 可以上升到刘海屏的状态栏,还需要加上 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 来确保view可以上移到状态栏。具体如下

    fun hideStatusBar(window: Window) {        //直屏手机一行就搞定        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)        //允许window 的内容可以上移到刘海屏状态栏        if (Build.VERSION.SDK_INT >= 28) {            val lp = window.attributes            lp.layoutInDisplayCutoutMode =                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES            window.attributes = lp        }        //将显示内容上移到状态栏        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN    }

WindowInsetsController方式实现:

        fun hideStatusBar3(window: Window) {        WindowInsetsControllerCompat(window, window.decorView).let {            //效果同SYSTEM_UI_FLAG_FULLSCREEN            it.hide(WindowInsetsCompat.Type.statusBars())            //维持隐藏的效果,hide 方法的隐藏会被下拉状态栏取消的。效果同 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY            it.systemBarsBehavior =                WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE        }        //允许window 的内容可以上移到刘海屏状态栏        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {            val lp = window.attributes            lp.layoutInDisplayCutoutMode =                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES            window.attributes = lp//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {                //将显示内容上移//                window.setDecorFitsSystemWindows(false)//            } else {                window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN//            }        }    }

此处 View.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 之所以不用 window.setDecorFitsSystemWindows(false) 代替,是因为 window.setDecorFitsSystemWindows(false) 还包含有 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION的作用。如果替代了,则效果如下:
在这里插入图片描述
可以看到底部的两行textview都被遮挡了。也就是页面内容延伸到了导航栏底部。

沉浸式状态栏

沉浸式状态栏就很容易理解了,就是把显示的内容上移到状态栏,但是又不隐藏状态栏。(当然直接修改状态栏的颜色,让它和背景一样也是可以的,但是这种仅仅在纯色的情况下实用,如果是图片类的背景就不适用了)。实现如下:

    fun transparentStatusBar(window: Window){        //去掉半透明的可能性        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)        //可以设置系统栏的背景色        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)        window.statusBarColor = Color.TRANSPARENT        //view的位置上移到系统栏        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE    }

看下效果图:
在这里插入图片描述
效果是实现了,但是有点小瑕疵,我们是不希望状态栏遮住我们的内容,但是如果每个页面都要手动计算状态栏的高度然后去设置padding 就过于麻烦了。很简单,在布局文件的根布局加上:

android:fitsSystemWindows="true"

这个属性只有设置了 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATIONView.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,才会有效果。
作用呢就是自动给view加上个对应系统栏高度的padding。看下效果:
在这里插入图片描述

隐藏导航栏

没啥好说的,直接上代码吧

        fun hideNavigation(window: Window) {        //将显示内容下移到导航栏        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY    }

WindowInsetsController方式实现:

        fun hideNavigation2(window: Window) {        //将显示内容下移到导航栏        WindowInsetsControllerCompat(window, window.decorView).let {            //效果同SYSTEM_UI_FLAG_HIDE_NAVIGATION            it.hide(WindowInsetsCompat.Type.navigationBars())            //维持隐藏的效果,hide 方法的隐藏会被下拉状态栏取消的。效果同 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY            it.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE        }    }

沉浸式导航栏

    fun transparentNavigationBar(window: Window) {        //去掉半透明的可能性        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)        //可以设置系统栏的背景色        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)        window.navigationBarColor = Color.TRANSPARENT        //view的位置下移到导航栏        window.decorView.systemUiVisibility =            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE    }

记得要配合 fitsSystemWindows 使用

在这里插入图片描述

隐藏状态栏以及导航栏

如果同时使用上面的两个方法组合是不行的。因为 window.decorView.systemUiVisibility 是赋值使用的,后一个会覆盖前一个的。实现如下:

        fun hideSystemBar(window: Window){        //直屏手机一行就搞定        window.decorView.systemUiVisibility =                    // 隐藏系统栏                    View.SYSTEM_UI_FLAG_FULLSCREEN or                    // 隐藏导航栏                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or                    //维持隐藏的效果                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or                    //将内容往上推到刘海屏的位置                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN        //允许window 的内容可以上移到刘海屏状态栏        if (Build.VERSION.SDK_INT >= 28) {            val lp = window.attributes            lp.layoutInDisplayCutoutMode =                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES            window.attributes = lp        }    }

WindowInsetsController方式实现:

        fun hideSystemBar2(window: Window) {        WindowInsetsControllerCompat(window, window.decorView).let {            //效果同SYSTEM_UI_FLAG_FULLSCREEN            it.hide(WindowInsetsCompat.Type.statusBars())            it.hide(WindowInsetsCompat.Type.navigationBars())            //维持隐藏的效果,hide 方法的隐藏会被下拉状态栏取消的。效果同 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY            it.systemBarsBehavior =                WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE        }        //允许window 的内容可以上移到刘海屏状态栏        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {            val lp = window.attributes            lp.layoutInDisplayCutoutMode =                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES            window.attributes = lp            //在不隐藏系统栏的情况下,将内容移动到系统栏            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {                //该方法会同时作用于状态栏以及导航栏                window.setDecorFitsSystemWindows(false)            } else {                window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN            }        }    }

在这里插入图片描述

沉浸式状态栏以及导航栏

        fun transparentSystemBar(window: Window) {        //去掉半透明的可能性        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)        //可以设置系统栏的背景色        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)        window.statusBarColor = Color.TRANSPARENT        window.navigationBarColor = Color.TRANSPARENT        //view的位置上移到系统栏        window.decorView.systemUiVisibility =                //将内容上移到状态栏            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or                    //将内容下移到状态栏                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE    }
        fun transparentSystemBar2(window: Window) {        //去掉半透明的可能性        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)        //可以设置系统栏的背景色        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)        window.statusBarColor = Color.TRANSPARENT        window.navigationBarColor = Color.TRANSPARENT        //在不隐藏系统栏的情况下,将内容移动到系统栏        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {            //该方法会同时作用于状态栏以及导航栏            window.setDecorFitsSystemWindows(false)        } else {            window.decorView.systemUiVisibility =                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION        }    }

记得要配合 fitsSystemWindows 使用
在这里插入图片描述

来源地址:https://blog.csdn.net/qq_32664007/article/details/126279919

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯