脑残安卓:状态栏

这是一系列介绍 Android 脑残特性的文章。Android 是一个满载先进科技的系统,同时也是一个充满碎片化、设计缺陷和执行缺陷的缺陷系统。

本篇文章针对 Android 对于状态栏的处理。

状态栏对于一个正常的智能操作系统的意义非凡,在 UI 设计上,很难避开对状态栏的处理,并且在对状态栏的处理上,各家很难有什么创意。一般来说,状态栏是一个用于摆放时间、系统状态图标和通知图标的条状界面,往往用于召唤通知中心的起点。

在 Android 应用上,各家对于状态栏的设计一向是比较分裂的,主要有以下几种:

黑色样式

黑色样式是经典的对于 Android 状态栏的处理方式。在 Android 2.x 时代,状态栏甚至还会有纹理背景,随后在 Android 3.0 Ice-cream Sandwich 引入 Holo Theme 后,将状态栏背景改为更加单一的黑色(或暗色)样式。黑色状态栏并非 Android 独有,早期的 iOS 也使用黑色(或白色)状态栏,从 iOS 6 开始才改为沉浸式。因此如果 UI 设计之初便是参考 iOS 的设计语言,采用黑色状态栏也就不难理解了。比如大量借鉴了旧版 iOS 的设计的 Smartisan OS,其内置软件一律采用黑色状态栏。

黑色状态栏的好处是通知区域和应用区域可以清晰区分,缺点是在白色机器上的一体感会大打折扣。


iOS 样式

iOS 从第六版开始,允许开发者使用沉浸式状态栏,即对状态栏进行上色(Tint),并且在 iOS 7 中将其设计语言发扬光大为全景状态栏,即:推荐开发者将状态栏的颜色与应用 Navigation Bar 的颜色设计为一致,从而创造出完美融合的效果。Google 在 Android 4.4 Kitkat 中,允许开发者变相实现沉浸式状态栏(实际上是为全屏应用设计的样式),因此这个时期的应用很多仿照 iOS 7 的样式,采用全景状态栏。

实际上在 Android 上实现全景状态栏并不是很优雅,因为在默认的 style.xml 中,如果将 ColorPrimary 和 ColorPrimaryDark 设置成同一个颜色:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">#00BCD4</item>
    <item name="colorPrimaryDark">#00BCD4</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

会发现状态栏上被染上了一个阴影,效果很不自然(可以看出 Google 是希望状态栏的颜色比工具栏的更深的)。况且,这个方法只适用于 Android 5.0 (API Level 21)及以上版本。

但是如果在 Java 代码中设置状态栏颜色:

getWindow().setStatusBarColor(Color.parseColor("#00BCD4"));

会发现状态栏变成了实色的,不但拉出汉堡菜单时不会被遮挡,而且会屏蔽所有动态效果。就相当于本来动态上色的状态栏,现在变成了一个没有生命的实色实体。

实际上绝大多数实现全景状态栏的应用并不是使用了上述的任意一种方法,而是将应用伪装成一个全屏应用,令状态栏透明,再手写双层宽度的 AppBar。具体的实现方法可以参考这篇文章。用这种方法实现的全景状态栏可以兼容 Android 4.4 Kitkat (API Level 20)以上。


Material 样式

Material 样式是与 Android 5.0 Lollipop 同期发布的 Material Design 中的推荐样式,是分歧最大的地方,也是本文的重点。

上文中提到,在 Android 5.0 Lollipop 及以上版本中,Google 推荐使用的默认 Material 主题(或者考虑兼容性,使用 App Compact 主题)中,状态栏的颜色是要比工具栏稍深的,并且在定义好颜色之后,程序还会默认为状态栏渲染一个细微的阴影以创造层次感。看起来,状态栏应该是位于工具栏下方的。

但是实际上,在 Google Design 的指导中,状态栏的 Elevation(高度)是在最高层的:

另一个强有力的证据是:在拥有汉堡菜单的应用中,(默认情况下)拉出汉堡菜单的过程中就会发现汉堡菜单是衬在状态栏下方的:

这就引起了很大的理解困难。假如没有那一层阴影(左),还有一丝希望让用户理解为状态栏是一层半透明的折罩;加入阴影之后(右),就连这一丝希望都没有了。

而当应用中存在汉堡菜单时,情况就变得雪上加霜:

这时的状态栏至少有两种解释:

  1. 状态栏在主界面以及汉堡菜单的上方,为暗色半透明;
  2. 状态栏在主界面上方,为暗色半透明;而在此之上存在汉堡菜单,汉堡菜单之上又有其自己对应的状态栏,为暗色半透明。

显然,较为合理的解释是第二种(虽然思考过程很拧巴、很不直观)。但根据 Google 官方“状态栏高于一切”的准则,第一种解释才是实际情况。

为了克服这种理解困难,有相当一部分应用选择在汉堡菜单中隐藏状态栏。方法是将应用伪装成全屏应用,将状态栏背景设置为全透明,但自己手动画一个假的状态栏出来,高度低于汉堡菜单。如下图:

这种方法很聪明,但是也很耗时耗力。而且一旦状态栏的通知图标稍多,使汉堡菜单上方有元素,一样会导致悖论。如上图中左上角的时间。

一些大牌应用的解决方案更是五花八门:

印象笔记(左一)干脆用了实色状态栏。既然怎么透明都不对,那我们干脆不透明好不好。虽然这样在理解上依然会造成一些困难(为什么本来更暗的状态栏在主界面变暗之后反而感觉是变亮了?),但是至少不会像默认样式那样让人有过高的空间崩塌感。

bilibili(左一)选择在主界面使用全景状态栏,但是在汉堡菜单上放一个暗色的遮罩。不得不说这一招非常漂亮,既不会让人觉得动画过程突兀,也给了通知图标一个合理的归宿。

更有甚者就像 BuzzFeed(右二)和 Apple Music(右一)这样,干脆状态栏就是全黑的。全程状态栏不在程序 UI 范围内,汉堡菜单也都在下方。沉浸感稍弱但至少没有诡异的视觉错觉,是非常稳重的做法。但是这样会让程序稍微缺少现代感。

可能有人会问:iOS 上不也有很多 Material Design 的应用吗?你为什么不说“脑残 iOS”,却偏针对 Android 呢?

喏,这就是证据。

更新:bilibili 的实现方法

上文提到 bilibili 在应用内使用全景状态栏,但是让侧面的汉堡菜单拥有一个淡淡的 Overlay。我今天在实验的时候巧合般实现了这个效果:

和 bilibili 一样,在平时显示为全景状态栏,而在调出汉堡菜单的时候,汉堡菜单本身带一个状态栏。
这也侧面验证了上述 PocketCast 状态栏理解问题的第二个猜测:两个 View 分别有自己的状态栏。这种实现方式背离 Google 的设计准则,看来 Google 的美工团队和开发团队交流也不是很通畅。

实现这种效果需要两步:

  1. 在 style.xml 中将 colorPrimary 和 colorPrimaryDark 设置为相同的颜色
  2. 给 Activity 对应的布局文件中对应的 AppBarLayout 添加属性:app:elevation=0dp

发表评论

电子邮件地址不会被公开。 必填项已用*标注