一个Android沉浸式状态栏上的黑科技

article/2025/8/24 8:24:24

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。

说起来,在不知不觉中,我竟然凑成了这沉浸式状态栏三部曲。

其实最开始的时候,我主要是因为工作上的原因想要在Android版的Edge浏览器上实现首页图片沉浸式的功能。

那么为了实现这个功能,我提前去做了一些技术调研,并将调研的结果整理成了一篇文章,具体可参阅 再学一遍android:fitsSystemWindows属性 。

做完技术调研之后,接下来就是功能实现了。对于Android版的Edge浏览器而言,首页图片的沉浸式一直是部分网友长久以来的呼声,经过我的各种攻坚和踩坑之后,终于将这个功能完成了。具体可参阅 我为Android版Microsoft Edge所带来的变化 。

实现沉浸式之后的效果如下图所示:

在这里插入图片描述

不过,有朋友在评论区提出了这样一个疑问:

在这里插入图片描述

确实,这是一个做沉浸式功能时比较容易被忽略的问题。如果背景图片的颜色和状态栏图标的颜色非常接近的话,那么的确会造成状态栏图标看不清楚的情况。

这里我举了一些沉浸式效果做得不太好的案例,具体是什么App我就不提了。

在这里插入图片描述
在这里插入图片描述

可以看到,这些App虽然实现了沉浸式状态栏的效果,但是由于状态栏上的图标变得难以看清,所以最终效果可能反而不好。

但是,Edge浏览器是不会存在这种问题的。为什么呢?这就是我在上篇文章中说的,在实现沉浸式状态栏时运用了一些小黑科技。那么借助这些小黑科技,我终于可以凑成这沉浸式状态栏三部曲了。

话不多说,下面技术开讲。

其实想要解决上图中的这种由于颜色值接近,导致部分内容看不清的情况,我能想到两种解决方案。一种是从设计层面解决,一种是从技术层面解决。

从设计层面解决相对会比较容易一些,同时应该也是大部分App会采用的方案,那就是在背景图的上方再盖一层阴影。有了这层阴影之后,我们可以让状态栏上的图标始终都是浅色的。即使出现浅色的背景图,由于阴影层的存在,状态栏上的图标依然是可以看得清的。

但如果只是用这个方案解决的话,那么我就不会写本篇文章了。因为这里我们会采用第二种方案,从技术层面解决。

首先从技术层面进行分析,要解决这个问题,无非就是需要将背景图颜色和状态栏图标的颜色区分开。

Android系统其实给了我们API来控制状态栏图标的颜色,但是只能设置成黑、白这两种颜色,而不可以将状态栏图标改成五颜六色的样子。

默认情况下,系统会认为我们拥有的是一个深色的状态栏,那么状态栏上面的图标自然就应该白色的,因为只有这样才能看得清上面的图标。

而调用如下API则可以让系统认为我们拥有的是一个浅色的状态栏:

private fun setLightStatusBar() {val flags = window.decorView.systemUiVisibilitywindow.decorView.systemUiVisibility = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}

如此一来,状态栏上面的图标就会变成黑色的,以和浅色的状态栏相互映衬。

如果要动态恢复成默认的深色状态栏,只需要这样设置:

private fun setDarkStatusBar() {val flags = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BARwindow.decorView.systemUiVisibility = flags xor View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}

这就是我们拥有的用于控制状态栏图标颜色的API。

好了,现在有了这个法宝来控制状态栏图标的颜色,那么接下来的问题就是,什么时候应该显示白色的状态栏图标?什么时候应该显示黑色的状态栏图标?

答案是显而易见的,为了能让前景背景的颜色区分更加明显,当然应该是底部是深色背景图的时候显示白色的状态栏图标,底部是浅色背景图的时候显示黑色的状态栏图标。

因此,现在的问题就转移成了,我们如何才能识别一张背景图的指定区域是属于深色还是浅色?

非常幸运,在Android系统上我们是可以做到这一点的,只需要借助Google提供的Palette库即可。

Palette是一个专门用于对图像进行颜色提取和识别的库,功能虽然不能说是非常强大,但是已经完全可以满足我们这里的需求了。

要使用Palette库,首先需要将它引入到项目当中,如下所示:

dependencies {implementation 'androidx.palette:palette:1.0.0'
}

接下来我们就可以借助Palette来进行一些颜色提取功能了,示例用法如下:

Palette.from(bitmap).setRegion(left, top, right, bottom).maximumColorCount(colorCount).generate {}

这是Palette最基础、最常见的用法。

首先,我们传入一个bitmap对象,这样Palette就会对它来进行图像解析。

然后调用setRegion()方法来指定解析这个bitmap对象的哪个区域。比方说我们本篇文章是要解决状态栏图标的问题,那肯定就要去解析手机状态栏那个区域的颜色值,其他区域的颜色值对我们来说没有意义。

接着调用maximumColorCount()方法来告诉Palette一共需要提取多少个颜色特征点。具体的颜色提取算法是由Palette自己控制的,我们无需关心。反正只需要知道,最终提取出来的这些颜色值都是这个bitmap的指定区域里最具代表性的就可以了。

最后调用generate()方法开始解析,Palette会开启异步线程来执行解析操作,并将最终结果回调到Lambda表达式当中。

现在我们已经得到这些提取出的特征点颜色值了,那么接下来,我们又该如何处理它们呢?

需要说明的事,后续的处理逻辑其实并没有一个非常严格的规定。我只说一下我个人的处理方式,大家也完全可以去定义自己的处理逻辑。

先贴一下代码,我再进行解释:

Palette.from(bitmap).maximumColorCount(colorCount).setRegion(left, top, right, bottom).generate {it?.let { palette ->var mostPopularSwatch: Palette.Swatch? = nullfor (swatch in palette.swatches) {if (mostPopularSwatch == null|| swatch.population > mostPopularSwatch.population) {mostPopularSwatch = swatch}}mostPopularSwatch?.let { swatch ->val luminance = ColorUtils.calculateLuminance(swatch.rgb)// 当luminance小于0.5时,我们认为这是一个深色值.if (luminance < 0.5) {setDarkStatusBar()} else {setLightStatusBar()}}}}

由于刚才在maximumColorCount()方法中传入了提取颜色特征点的数量,因此generate()方法的回调当中我们就可以得到多个颜色特征点(Swatch)。

而每个颜色特征点都会有一个权重值,调用getPopulation()方法可以获取,表示该特征点在选定的bitmap区域的重要程度。我选取了权重值最高的那个特征点来作为这个bitmap区域的代表颜色值。

接下来再调用ColorUtils.calculateLuminance()方法来计算选取的这个颜色值的亮度。当亮度低于0.5时,我就认为这是一个深色的颜色值,那么此时将状态栏设置成深色模式,状态栏图标就会自动变成白色。反之就将状态栏设置成浅色模式,此时状态栏图标就会自动变成黑色。

大概流程就是这个样子,我觉得原理还是非常简单的,我甚至都没有给出一个完整的实例,只是贴出了一些代码片段。

至于Palette,终归只是一个比较小众的库,知道或使用过的人可能并不多,所以用上这种小众技术我觉得足以称得上是黑科技了。

那么最后我们就来看一看实际的运行效果吧。

这里我准备了几张不同的背景图,由Palette解析之后,会根据识别出的颜色值动态更改状态栏图标的颜色。

这是深色背景图的效果。

在这里插入图片描述

这是浅色背景图的效果。

在这里插入图片描述

可以看到,不管在什么背景图下,状态栏图标的颜色都可以做到自动适配,保证图标始终是清晰可见的。

目前这种使用Palette来动态进行颜色识别的方案,我感觉至少是可以保证99%以上的场景都能够正确适配的,但是也存在一些特别极端的场景。

比如说背景图就是一张黑白左右分割的图片,这种情况下Palette会选取哪种颜色来作为代表色其实是不确定的。但不管是选中了黑还是白,都一定会导致状态栏上有一半区域的图标是不可见的。效果如下图所示:

在这里插入图片描述

不过对于这种极端情况,我觉得就没必要强求了。甚至我都并不认为这是一个Bug,反而觉得这是一种很酷的效果,你们觉得呢?

好的,本篇文章就到这里。文中我只帖出了所有关键代码的示例,以及最终运行效果的截图。如果你不想自己动手去敲一遍,也可以直接参考我的完整源码:

https://github.com/guolindev/ImmersiveStatusBar

沉浸式状态栏三部曲到此完结。

如果想要学习Kotlin和最新的Android知识,可以参考我的新书 《第一行代码 第3版》,点击此处查看详情。


关注我的技术公众号,每天都有优质技术文章推送。

微信扫一扫下方二维码即可关注:


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

相关文章

Android 实现沉浸式状态栏

上一篇文章将Android 实现变色状态栏我们实现了变色的状态栏&#xff0c;也介绍了沉浸式状态栏和透明状态栏的区别&#xff0c;这篇文章我们实现沉浸式状态栏。 沉浸式状态栏的来源就是很多手机用的是实体按键&#xff0c;没有虚拟键&#xff0c;于是开了沉浸模式就只有状态栏消…

Android 沉浸式状态栏攻略 让你的状态栏变色吧

转载请标明出处&#xff1a; http://blog.csdn.net/lmj623565791/article/details/48649563&#xff1b; 本文出自:【张鸿洋的博客】 一、概述 近期注意到QQ新版使用了沉浸式状态栏&#xff0c;ok&#xff0c;先声明一下&#xff1a;本篇博客效果下图&#xff1a; 关于这个状…

Android 最新实现沉浸式状态栏的效果

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家&#xff0c; &#x1f449;点击跳转到网站 什么是状态栏: 是指手机屏幕最顶上&#xff0c;显示中国移动、安全卫士、电量、网速等等&#xff0c;在手…

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

国内很多类似的文章, 我只想说一个字, 真tm乱! 我看不懂… 评论里面 全在说无效什么的 (我试了也无效, 好厉害的样子) 不废话,回到正题, 首先贴上一个众所周知的库 SystemBarTint 我只要这个类 https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readys…

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…