何为透明状态栏?有人说透明状态栏就是沉浸式状态栏,这里不多解释,详见为什么在国内会有很多用户把「透明栏」(Translucent Bars)称作 「沉浸式顶栏」?。本文将对于透明状态栏设置方法做一个讲解,以及遇到的问题该怎么解决,来面对各种需求。
本文所提到的透明状态栏其实指的是将顶部的导航栏延伸到状态栏,使之浑然一体(Google官方建议状态栏颜色比导航栏的颜色略深一点),并不代表一定不设置背景色,比如导航栏是白色,则可设置状态栏为白色,视情况而定。
相比于iOS系统,Android系统对于状态栏的设置就显得稍微复杂了一点。Android系统提供了API 19以上对状态栏的设置接口,而直到API 23以上才提供对于icon颜色的设置,还有就是各家厂商(如魅族,小米等)对于状态栏的有自己的定制,对于需要使用浅色背景状态栏的应用,没处理好的话往往导致浅色背景,白色icon ,状态栏不分你我的悲剧。。
(内心os:嗯?右上角那一个绿色的电池,用户一定知道他是状态栏对吧。)
我随即对比了一些主流app,发现在我的魅蓝2(Android 5.1 Flyme 4.5)上竟然都不支持透明状态栏,这对于我这种追求审美的人(其实是视觉提的需求)来说简直不能忍。在我折腾了几天之后,终于解决了这些问题,希望对大家思路有一些帮助。
言归正传,本文主要针对以下几点进行分析:1.是否隐藏状态栏(全屏模式) 2.状态栏的背景色的设置 3.状态栏icon的颜色的设置,而对于透明状态栏设置过程中,可能造成的icon颜色设置成功,而背景颜色设置失败,等等原因造成的浅色底,浅色字或深色底,深色字等错误情况的处理及兜底方案 4.源码实现。
下面我们就以上几点来讨论一下Android中透明状态栏的实现。
1. 全屏模式(沉浸式状态栏)
这种情况其实用得并不多,基本上使用场景在闪屏页展示广告或logo,以及一些阅读类app需要尽可能的利用到屏幕大小,展示更多的内容。
设置方法很简单,分两种,在API > 16时:
theme中定义:将自定义该Activtiy的theme,并在其中添加
1
<item name="android:windowFullscreen">true</item>
代码中定义:
1
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
2. 状态栏的背景色
我们都知道,在Android5.0(即API > 21)时,Google官方提供接口设置对应的状态栏背景色
1 | window.setStatusBarColor(int color); |
那我们要想在Android5.0以下设置背景色就真的没有办法了吗?并不是,我们发现在Android4.4以后,出现了windowTranslucentStatus这一特性,所以思路如下:
- 先设置状态栏的透明属性
- 给根布局顶部加上一个和状态栏一样大小的矩形View,充当假的状态栏
- 设置FitsSystemWindows属性为true,此时跟布局会延伸到状态栏,处在顶部位置就是之前设置的view,这样就可以以假乱真了。
1 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { |
设置矩形色块的代码如下:
1 | private static View createStatusView(Activity activity, int color) { |
效果如下:
3. 状态栏icon的颜色
我们通过调研发现,魅族和小米的两个厂商分别对于Flyme及MIUI做了状态栏的定制,所以我们可以在Flyme4.0以上及MIUIV6以上都可以实现icon颜色的替换,结合上述的状态栏背景色的替换,我们就可以对于魅族及小米两款机型适配浅色的导航栏了。
3.1 魅族Flyme4.0+
1 | /** |
3.2 MIUIV6.0+
1 | /** |
效果如下:
4. 设置背景色失败处理方案
4.1 解决思路
说了那么多,有童鞋可能就问了,上述说的代码可能对大多数机型有效,但是万一某些机型设置失败了呢?体验不但没提升,反倒让用户不能忍受了。
好吧,下面我们来看一下应该怎么解决。思路如下:
- 获取状态栏的背景色,与需要设置的颜色进行比对,如果相等则设置成功。
- 如果背景色设置失败,针对非Flyme机型,我们回退状态栏icon颜色,还原到最初状态。
- 上述为什么要强调非Flyme机型,是因为用过Flyme机型的童鞋肯定知道有一个透明状态栏的开关,当开启后,Flyme会对状态栏进行处理,但处理效果并不和我们想要的一样(比如视图顶部为黑色背景,但是却设置了白色状态栏),并且两者都对状态栏进行操作,操作的冲突也有可能引起上述的icon颜色不明显的情况,所以我们自己定义一个ColorDrawable进行替换。
4.2 获取状态栏颜色
1 | /** |
下面讲一下针对如何屏蔽Flyme机型沉浸式状态栏打开时的影响:
1 | private static boolean setMeizuStatusColor(Window window, @ColorInt int color, int alpha) { |
4.3 兜底方案
上面提到了一些失败判断途径,那么如果还是对一些机型无效呢?我的建议是可以在本地预埋一个开关,万一出问题,服务器可以对相应机型的沉浸式开关关掉即可,或者采用hotfix等方式来进行修复。
5. 源码分析
看过上面针对魅族和小米设置icon的源代码我们知道,共同点都是通过反射来进行调用相应的方法,或者对于相应的状态栏视图进行设置颜色。
我们来看看源码,在Window中有一个抽象方法是setStatusBarColor而在PhoneWindow中我们找到了该方法的实现
1 |
|
mDecor.updateColorViews()
1 | private WindowInsets updateColorViews(WindowInsets insets, boolean animate) { |
那么mNavigationColorViewState和mStatusColorViewState又是什么呢?
别着急,我们继续往下看
1 | private final ColorViewState mStatusColorViewState = new ColorViewState( |
mNavigationColorViewState和mStatusColorViewState这两个都是ColorViewState定义好的ColorViewState,主要存储了关于statusbar的一些视图信息。包括id,translucentFlag,verticalGravity等等。
接着看updateColorViewInt()方法,这个方法是对一个View设置颜色的,自然这个View也就是指的状态栏的View,下面来看一下实现:
1 | private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, |
6. 总结
话说了这么多,其实就是想让大家知道为什么很多app放弃在较低版本的Android系统对状态栏进行视觉修改,尤其是浅色导航栏的应用。[捂脸]