一个android沉浸式状态栏上的黑科技(代码片段)

guolin guolin     2022-12-07     554

关键词:

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

private fun setLightStatusBar() 
    val flags = window.decorView.systemUiVisibility
    window.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_BAR
    window.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? = null
            for (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版》点击此处查看详情


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

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

一个android沉浸式状态栏上的黑科技(代码片段)

...实最开始的时候,我主要是因为工作上的原因想要在Android版的Edge浏览器上实现首页图片沉浸式的功能。那么为了实现这个功能,我提前去做了一些技术调研&# 查看详情

android---沉浸式状态栏(代码片段)

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

android--------沉浸式状态栏immersionbar3.0(代码片段)

...使用的一个库ImmersionBar,非常不错,现在都3.0了android4.4以上沉浸式状态栏和沉浸式导航栏管理,适配横竖屏切换、刘海屏、软键盘弹出等问题,可以修改状态栏字体颜色和导航栏图标颜色,以及不可修改字体... 查看详情

android--------沉浸式状态栏immersionbar3.0(代码片段)

...使用的一个库ImmersionBar,非常不错,现在都3.0了android4.4以上沉浸式状态栏和沉浸式导航栏管理,适配横竖屏切换、刘海屏、软键盘弹出等问题,可以修改状态栏字体颜色和导航栏图标颜色,以及不可修改字体... 查看详情

android沉浸式状态栏(代码片段)

在使用沉浸式状态栏时需要注意设置的沉浸颜色为白色还是其他深色的区别1.状态栏字体图标黑色(深色)//5.0以上系统状态栏透明if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)Windowwindow=getWindow();window.clearFlags(WindowMan... 查看详情

android沉浸式状态栏的实现(代码片段)

...0c;第一个浮现在脑海里的词就是“碎片化”。碎片化是让Android开发者很头疼的问题,相信没有哪位开发者会不喜欢“writeonce,runanywhere”的感觉,碎片化让我们不得不耗费精力去校验代码在各个系统版本、各个机型上是否... 查看详情

android实现沉浸式全屏(代码片段)

前言本文总结Android实现沉浸式全屏的实现方式。实现沉浸式全屏在一些需要全屏显示的场景下,比如玩游戏、看横屏视频的时候,内容全屏,占满窗口的体验会让用户更加沉浸到对内容的消费中,带来好的用户体验。沉浸式显... 查看详情

android沉浸式状态栏(代码片段)

文章目录前言一、沉浸式状态栏二、改变标题和状态栏颜色总结前言4-20分享提示:以下是本篇文章正文内容,下面案例可供参考先来看下效果图(夜神模拟器和真机有点差别,我不会搞gif动图,就勉强看下吧):一、沉浸式状... 查看详情

titlelayout——一个android轻松实现通用标准支持沉浸式状态栏的标题栏库(代码片段)

TitleLayout多功能、通用的、可在布局或者使用Java代码实现标题栏;支持沉浸式状态栏,支持左侧返回按钮(不需要手动实现页面返回),左侧支持图片+文字、图片、文字;右侧支持图片、文字等。堆码不易,star支持,万分感谢... 查看详情

android如何实现5.0以上图片沉浸式状态栏(代码片段)

Android如何实现5.0以上图片沉浸式状态栏1.设置Theme<stylename="TranslucentTheme"parent="Theme.AppCompat.Light.NoActionBar"> <itemname="android:windowTranslucentNavigation"&g 查看详情

android沉浸式(透明)状态栏细研-超级细还附demo(代码片段)

前言在Android4.4中引入了沉浸模式的功能,但这个版本非真正的沉浸模式,应该说是透明模式。Android5.0以后才可以在系统层面实现真正的沉浸式状态栏。沉浸式状态栏是为了与当前使用的App页面风格统一,不会显的那... 查看详情

android沉浸式(透明)状态栏细研-超级细还附demo(代码片段)

前言在Android4.4中引入了沉浸模式的功能,但这个版本非真正的沉浸模式,应该说是透明模式。Android5.0以后才可以在系统层面实现真正的沉浸式状态栏。沉浸式状态栏是为了与当前使用的App页面风格统一,不会显的那... 查看详情

android实现沉浸式状态栏的效果(代码片段)

...体验。下面我来介绍一下一、标题栏延伸到状态栏适用于Android6.0+因为我的标 查看详情

android开发笔记沉浸式状态栏(代码片段)

在Android开发中我们越来越重视用户的App操作体验,在使用App中我们主张减少对用户的干扰,经常会提到一致性体验。为了追求界面的风格的一致性,Google官方在Android4.4开始,支持了系统最上方的状态栏(StatusBar)和... 查看详情

android沉浸式状态栏的实现(代码片段)

...0c;第一个浮现在脑海里的词就是“碎片化”。碎片化是让Android开发者很头疼的问题,相信没有哪位开发者会不喜欢“writeonce,runanywhere”的感觉,碎片化让我们不得不耗费精力去校验代码在各个系统版本、各个机型上是否... 查看详情

android透明状态栏和沉浸式的实现

Android透明状态栏和沉浸式的实现1.什么是沉浸式根据百度百科上的定义,沉浸式就是要给用户提供完全沉浸的体验,使用户有一种置身于虚拟世界之中的感觉。其真正含义并不是大家所想的那样,对屏幕状态栏进行一些操作,甚至... 查看详情

android沉浸式状态栏(代码片段)

在使用沉浸式状态栏时需要注意设置的沉浸颜色为白色还是其他深色的区别1.状态栏字体图标黑色(深色)//5.0以上系统状态栏透明if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)Windowwindow=getWindow();window.clearFlags(WindowMan... 查看详情

关于如何彻底搞定androidkitkat+沉浸式状态栏效果(代码片段)

...oActionBar的,标题栏使用的是Toolbar控件,请知悉。Android4.4Android4.4以前的版本,状态栏的颜色都是黑色的&# 查看详情