Android 沉浸式状态栏完美解决方案

article/2025/8/24 8:22:41

国内很多类似的文章, 我只想说一个字, 真tm乱! 我看不懂… 评论里面 全在说无效什么的 (我试了也无效, 好厉害的样子)

不废话,回到正题, 首先贴上一个众所周知的库 SystemBarTint
我只要这个类
https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java
然后复制到你的工程
这里写图片描述
这个类我就不多说了, 就是兼容4.x以上沉浸透明状态栏的 一个兼容类

1.开始

先贴工具类, 有部分代码参考自网上并有做改动,

 
public class StatusBarUtil {public final static int TYPE_MIUI = 0;public final static int TYPE_FLYME = 1;public final static int TYPE_M = 3;//6.0@IntDef({TYPE_MIUI,TYPE_FLYME,TYPE_M})@Retention(RetentionPolicy.SOURCE)@interface ViewType {}/*** 修改状态栏颜色,支持4.4以上版本** @param colorId 颜色*/public static void setStatusBarColor(Activity activity, int colorId) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {Window window = activity.getWindow();window.setStatusBarColor(colorId);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//使用SystemBarTintManager,需要先将状态栏设置为透明setTranslucentStatus(activity);SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity);systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色}}/*** 设置状态栏透明*/@TargetApi(19)public static void setTranslucentStatus(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色Window window = activity.getWindow();View decorView = window.getDecorView();//两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;decorView.setSystemUiVisibility(option);window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);window.setStatusBarColor(Color.TRANSPARENT);//导航栏颜色也可以正常设置//window.setNavigationBarColor(Color.TRANSPARENT);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {Window window = activity.getWindow();WindowManager.LayoutParams attributes = window.getAttributes();int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;attributes.flags |= flagTranslucentStatus;//int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;//attributes.flags |= flagTranslucentNavigation;window.setAttributes(attributes);}}/***  代码实现android:fitsSystemWindows** @param activity*/public static void setRootViewFitsSystemWindows(Activity activity, boolean fitSystemWindows) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {ViewGroup winContent = (ViewGroup) activity.findViewById(android.R.id.content);if (winContent.getChildCount() > 0) {ViewGroup rootView = (ViewGroup) winContent.getChildAt(0);if (rootView != null) {rootView.setFitsSystemWindows(fitSystemWindows);}}}}/*** 设置状态栏深色浅色切换 */public static boolean setStatusBarDarkTheme(Activity activity, boolean dark) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {setStatusBarFontIconDark(activity, TYPE_M, dark);} else if (OSUtils.isMiui()) {setStatusBarFontIconDark(activity, TYPE_MIUI, dark);} else if (OSUtils.isFlyme()) {setStatusBarFontIconDark(activity, TYPE_FLYME, dark);} else {//其他情况return false;}return true;}return false;}/*** 设置 状态栏深色浅色切换*/public static boolean setStatusBarFontIconDark(Activity activity, @ViewType int type,boolean dark) {switch (type) {case TYPE_MIUI:return setMiuiUI(activity, dark);case TYPE_FLYME:return setFlymeUI(activity, dark);case TYPE_M:default:return setCommonUI(activity,dark);}}//设置6.0 状态栏深色浅色切换public static boolean setCommonUI(Activity activity, boolean dark) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {View decorView = activity.getWindow().getDecorView();if (decorView != null) {int vis = decorView.getSystemUiVisibility();if (dark) {vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;} else {vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;}if (decorView.getSystemUiVisibility() != vis) {decorView.setSystemUiVisibility(vis);}return true;}}return false;}//设置Flyme 状态栏深色浅色切换public static boolean setFlymeUI(Activity activity, boolean dark) {try {Window window = activity.getWindow();WindowManager.LayoutParams lp = window.getAttributes();Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");darkFlag.setAccessible(true);meizuFlags.setAccessible(true);int bit = darkFlag.getInt(null);int value = meizuFlags.getInt(lp);if (dark) {value |= bit;} else {value &= ~bit;}meizuFlags.setInt(lp, value);window.setAttributes(lp);return true;} catch (Exception e) {e.printStackTrace();return false;}}//设置MIUI 状态栏深色浅色切换public static boolean setMiuiUI(Activity activity, boolean dark) {try {Window window = activity.getWindow();Class<?> clazz = activity.getWindow().getClass();@SuppressLint("PrivateApi") Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");int darkModeFlag = field.getInt(layoutParams);Method extraFlagField = clazz.getDeclaredMethod("setExtraFlags", int.class, int.class);extraFlagField.setAccessible(true);if (dark) {    //状态栏亮色且黑色字体extraFlagField.invoke(window, darkModeFlag, darkModeFlag);} else {extraFlagField.invoke(window, 0, darkModeFlag);}return true;} catch (Exception e) {e.printStackTrace();return false;}}//获取状态栏高度public static int getStatusBarHeight(Context context) {int result = 0;int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");if (resourceId > 0) {result = context.getResources().getDimensionPixelSize(resourceId);}return result;}
}

好了,这个类 支持了 设置状态栏透明, 设置状态栏颜色, 支持了状态栏深色浅色切换(则状态栏上的文字图标颜色)

怕搞了一堆在别的文章的配置,所以我还是要说下以下代码不能出现:
全局搜索你的代码里 是否有 android:fitsSystemWindows , 删掉!
检查你的values、values-v19、values-v21等 是否配置了
如下item标签

// values-v19。v19 开始有 android:windowTranslucentStatus 这个属性
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar"><item name="android:windowTranslucentStatus">true</item><item name="android:windowTranslucentNavigation">true</item>
</style>// values-v21。5.0 以上提供了 setStatusBarColor()  方法设置状态栏颜色。
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar"><item name="android:windowTranslucentStatus">false</item><item name="android:windowTranslucentNavigation">true</item><!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色--><item name="android:statusBarColor">@android:color/transparent</item>
</style>

凡是在style.xml中 有关 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 统统删掉
因为 我们要通过代码去实现, xml中的各种属性全部不要写, 避免代码出现互相干扰, 出现各种 无效的bug
好了干扰已全部去除,开始适配

首先在Activity 的setContentView 下一行编写如下代码

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.xxx);//设置状态栏透明StatusBarUtil.setTranslucentStatus(this);//一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清//所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) {//如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明,//这样半透明+白=灰, 状态栏的文字能看得清StatusBarUtil.setStatusBarColor(this,0x55000000);} 
}

2.重新认识fitsSystemWindows

有没有遇到过一个问题: 写一个布局 想要背景图片沉浸进去, 但是我又想要图片沉浸进去但内容如何控制不要沉浸进去?
一开始我搞笑的弄了个自定义View 里面内置了paddingTop=状态栏高度 的功能, 效果挺好, 后来发现其实官方已经帮我们做好了 只是我们用不好fitSystemWindows而已… 现在跟我分析分析fitSystemWindows的原理

为了理解这个玩意的作用, 我看了下源码, 原来fitsSystemWindows=true可以理解为 paddingTop=状态栏高度, =false可以理解为沉浸进状态栏
当然你直接设置肯定没效果 你还要设置我上面说的StatusBarUtil.setTranslucentStatus(this);, 其实关键代码就是

        window.statusBarColor = Color.TRANSPARENTwindow.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

把窗口全屏而已

现在来说说他们直接是如何传递的, fitsSystemWindows在ViewGroup中通过dispatchApplyWindowInsets进行分发给子View
如果dispatchApplyWindowInsets 中把insets.consumeSystemWindowInsets()消费掉, 那么inset事件就无法传递到子View,子View设置fitsSystemWindows=true 将会没有反应,View会通过onApplyWindowInsets消费掉WindowInsets, 当然其要求是父View必须设置fitSystemWindows=false 这样WindowInsets才能传递到子View中进行消费

那么, fitSystemWindows是如何实现paddingTop效果的呢?
源码发现
在这里插入图片描述
在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/d496d0440bbc40af96aeb0727a16dc44.png
computeFitSystemWindows 方法把系统的状态栏高度, 导航栏高度等计算 最终放到localInsets 这个rect中, 此时的rect.top就是状态栏高度
然后通过applyInsets->internalSetPadding, 把mPaddingTop=localInsets.top 完成状态栏高度的padding

至此 简单的分析了fitSystemWindows的作用
那么我们具体如何控制呢, 其实很简单 我们先来看一个错误的示范
在这里插入图片描述
如图, 很多时候我们的既有认知认为是设置为子View就有效果…

其实…前面看源码说了 ,他是对子View再加paddingTop=状态栏高度
所以正确的做法是再包裹一层

在这里插入图片描述

完事. 这样你就可以随心所欲控制哪个View 要沉浸 哪个不沉浸
当然如果需要动态控制的话 可以重写父View dispatchApplyWindowInsets 进行分发控制

================================================================

================================================================

补充:

================================================================

================================================================
值得注意的是 如果你按我那样去做, 状态栏颜色无法被修改, 请检查上层布局是否设置了背景
或者受了全局主题的

<item name="android:windowBackground">@color/xxx</item>

的颜色影响

感谢@Narbolo 的提醒, 漏了个Rom类型判断的工具类,现在贴上


public class OSUtils {public static final String ROM_MIUI = "MIUI";public static final String ROM_EMUI = "EMUI";public static final String ROM_FLYME = "FLYME";public static final String ROM_OPPO = "OPPO";public static final String ROM_SMARTISAN = "SMARTISAN";public static final String ROM_VIVO = "VIVO";public static final String ROM_QIKU = "QIKU";private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";private static final String KEY_VERSION_EMUI = "ro.build.version.emui";private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";private static String sName;private static String sVersion;public static boolean isEmui() {return check(ROM_EMUI);}public static boolean isMiui() {return check(ROM_MIUI);}public static boolean isVivo() {return check(ROM_VIVO);}public static boolean isOppo() {return check(ROM_OPPO);}public static boolean isFlyme() {return check(ROM_FLYME);}public static boolean is360() {return check(ROM_QIKU) || check("360");}public static boolean isSmartisan() {return check(ROM_SMARTISAN);}public static String getName() {if (sName == null) {check("");}return sName;}public static String getVersion() {if (sVersion == null) {check("");}return sVersion;}public static boolean check(String rom) {if (sName != null) {return sName.equals(rom);}if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {sName = ROM_MIUI;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {sName = ROM_EMUI;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {sName = ROM_OPPO;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {sName = ROM_VIVO;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {sName = ROM_SMARTISAN;} else {sVersion = Build.DISPLAY;if (sVersion.toUpperCase().contains(ROM_FLYME)) {sName = ROM_FLYME;} else {sVersion = Build.UNKNOWN;sName = Build.MANUFACTURER.toUpperCase();}}return sName.equals(rom);}public static String getProp(String name) {String line = null;BufferedReader input = null;try {Process p = Runtime.getRuntime().exec("getprop " + name);input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);line = input.readLine();input.close();} catch (IOException ex) {return null;} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return line;}
}

http://chatgpt.dhexx.cn/article/bjfloDqT.shtml

相关文章

Android---沉浸式状态栏

Android—沉浸式状态栏 我们的征程是星辰大海&#xff0c;而非人间烟尘 文章目录 Android---沉浸式状态栏去掉标题栏效果 引入依赖沉浸状态栏颜色沉浸状态栏图片 去掉标题栏 首先去掉对应主题下面的Android自带的ActionBar&#xff0c;只需要在对应主题下面加NoActionBar 效果 …

史上最完美的Android沉浸式状态导航栏攻略

前言 最近我在小破站开发一款新App&#xff0c;叫高能链。我是一个完美主义者&#xff0c;所以不管对架构还是UI&#xff0c;我都是比较抠细节的&#xff0c;在状态栏和导航栏沉浸式这一块&#xff0c;我还是踩了挺多坑&#xff0c;费了挺多精力的。这次我将我踩坑&#xff0c…

Android设置“沉浸式状态栏”的方法

Android系统界面构成 Android系统界面由状态栏、ActionBar和导航栏组成&#xff0c;如下图所示&#xff1a; “沉浸式状态栏”的界面显示效果 沉浸式状态栏&#xff0c;或叫“透明状态栏”&#xff0c;显示效果如下图所示&#xff1a; 实现沉浸式状态栏的步骤 一、添加StatusB…

Android沉浸式状态栏(透明系统状态栏)

Android沉浸式状态栏&#xff08;透明系统状态栏&#xff09;的目的&#xff1a;顶部系统状态栏和App的导航栏一体化&#xff0c;不给用户突兀的感觉&#xff0c;使用户把更多的视角留在我们的App上。 沉浸式状态栏的兼容情况 Android版本透明状态栏<4.44.4-5.0✓>5.0✓…

沉浸式状态栏

沉浸式状态栏 简单介绍 沉浸式状态栏其实是对沉浸模式(Immersive Mode)在状态栏上的体现,即让状态栏变透明,或者是在打游戏和看视频时彻底隐藏起状态栏,让用户拥有良好的体验。 比如说我们看视频,状态栏隐藏后视频的观看会更加完整,下图就是看视频时的截图,可以看到当…

Android沉浸式状态栏,看完这篇就够了!

背景 之前做过Android沉浸式状态栏的相关需求&#xff0c;但是一直忙于工作&#xff0c;没时间系统的整理下沉浸式相关的知识&#xff0c;所以今天抽出时间&#xff0c;写一篇 Android沉浸式状态栏的文章。 何为沉浸式 沉浸式就是要给用户提供完全沉浸的体验&#xff0c;使用…

图片预览与下载

后端返回base64进行图片的预览和下载下载 有一个图片预览的接口。后端返回的值长这个样子 1、预览 如果要在线预览的话&#xff0c;代码需要这样写放入img的src this.imgPreviewBaseUrl data:image/png;base64, res.data标签中就是这样的 2、下载 图片预览的这样的返…

vue 实现图片预览

vue实现图片预览 现在很多的项目里面图片展示缩略图,然后点击实现图片预览,放大的功能 最近我的项目里面就遇见了这么个场景,我选用了插件进行处理 下面说下实现步骤 1、首先安装插件 npm install vue-photo-preview --save插件地址 2、在main.js里面引入,全局使用 impor…

图片预览器

此预览器&#xff0c;一次只能翻页十张图片&#xff0c;开发人员可根据自己需要&#xff0c;修改前台代码。 页面代码 <% Page Language"C#" AutoEventWireup"true" CodeBehind"UpFilesAndPreview.aspx.cs" Inherits"WebFramework.Comm…

v-viewer图片预览

安装 npm install v-viewer引入 import Viewer from v-viewer import viewerjs/dist/viewer.css Vue.use (Viewer, {defaultOptions: {navbar: true,toolbar: false,movable: false,title: false,zoomable: true,scalable: true,transition: true} })使用 方法一&#xff1a…

vue2图片预览插件

文章目录 学习链接准备工作准备图片与基础的样式PrevImg.vueApp.vue静态效果 封装插件修改PrevImg.vue定义插件逻辑main.js中应用此插件App.vue中使用此插件 图片预览列表修改App.vue修改插件逻辑修改PrevImg.vue 学习链接 vue插件开发实例-图片预览插件 vue2-pre-img-plugin的…

vue 图片预览插件

vue 图片预览插件 步骤 1、先安装依赖 npm install v-viewer --save2、main.js内引用并注册调用 //main.js import Viewer from v-viewer import viewerjs/dist/viewer.cssVue.use(Viewer); Viewer.setDefaults({Options: { "inline": true, "button": …

Vue3图片预览(Image)

本图片预览组件主要包括以下功能&#xff1a; 展示图片时&#xff0c;可设置鼠标悬浮时的预览文本&#xff1b;图像无法加载时要显示的描述&#xff1b;自定义图像高度和宽度&#xff1b;设置图像如何适应容器高度和宽度&#xff08; fill(填充) | contain(等比缩放包含) | cov…

Vue实现图片预览和缩放功能

使用v-viewer插件即可实现图片预览和缩放功能。&#xff08;v-viewer是一款支持vue项目中的图片浏览组件&#xff0c;它支持图片旋转、缩放、翻转等操作&#xff0c;支持配置化.非常强大&#xff09; 安装依赖 执行命令&#xff1a; npm install v-viewer --save 引入并使用…

图片上传预览功能

大多时候我们上传图片&#xff0c;都是直接上传到服务器上&#xff0c;然后返回图片资源所在服务器的路径&#xff0c;然后页面根据这个路径&#xff0c;给图片一个src 属性就能看到图片了。 但是&#xff0c;这样会有一个弊端&#xff1a;如果客户对自己上传的图片不满意&am…

uniapp 图片预览实现

uniapp 图片预览实现 提示&#xff1a;下面案例可供参考 一、app端 使用 uni.previewImage 进行图片预览 可去 uniapp官网 查看具体使用:(https://uniapp.dcloud.net.cn/api/media/image.html#unipreviewimageobject) 二、具体使用如下&#xff08;示例&#xff09;&#xf…

Android中点击图片进行图片预览功能的实现(ImagePreview图片预览工具库)

因为需求&#xff0c;对于轮播图进行一个点击预览的功能&#xff0c;看了好多的文章&#xff0c;都没有弄出来&#xff0c;还是多亏了昊昊猿博主 点击查看原文 终于把它搞定啦。 这里的话是使用ImagePreview&#xff0c;一个非常好用的图片预览工具库&#xff08;还可以实现双…

Vue实现图片预览(Viewer.js)

Viewer.js viewer.js是一款开源的图片预览插件&#xff0c;功能十分强大: 支持移动设备触摸事件 支持响应式 支持放大/缩小 支持旋转&#xff08;类似微博的图片旋转&#xff09; 支持水平/垂直翻转 支持图片移动 支持键盘 支持全屏幻灯片模式&#xff08;可做屏保&#xff0…

前端图片预览怎么做?Vue

选择的图片文件&#xff0c;要给到img标签上纯前端的预览 让用户更近一步看到自己选择的头像 因为img的标签的src的值只能是以下两个 1、只能是图片的“链接地址”&#xff08;外链http://开头&#xff0c;图片文件相对路径&#xff09;(不能发给后台)因为第一种转成http是存…

点击图片实现大图预览

实现点击图片预览 这次主要是在table表格里能够实现&#xff0c;点击里面的图片实现大图预览的效果&#xff0c;直接找的现成的轮子&#xff0c;用在项目里。这里主要是讲讲用viewer组件实现点击图片预览&#xff0c;用的组件&#xff0c;只需在本地安装或者引入。 先看一下&…