“沉浸式”体验?异形屏适配?我把他们扒光了明明白白告诉你应该这样做

看似复杂的沉浸式体验设计,其实也就是在处理以下两个 System UI与用户布局(setContentView)之间说不清理还乱的关系:

  • StatusBar 系统状态栏
  • NavigationBar 系统导航栏

网上类似“沉浸式状态栏”的文章一搜一大把,且先不吐槽所谓的“沉浸式状态栏”说法正确与否,随便一篇文章都会告诉你,要实现沉浸式体验有两个关键地方:

  • window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_STABLE
  • window.statusBarColor = Color.TRANSPARENT

运行代码,果然实现了“沉浸式状态栏”的效果。可是我相信大多数人心里是迷糊的:

  • SYSTEM_UI_FLAG_LAYOUT_FULLSCREENSYSTEM_UI_FLAG_LAYOUT_STABLE 是什么?
  • 为什么要设置状态栏颜色透明?设置其他颜色不可以么?

以及接下来可能碰到的各种“沉浸式状态栏”带来的坑:

  • Bull shit!用户布局 与 状态栏信息 重叠了
  • 可交互按钮被手机的“刘海”给挡住了
  • 状态栏上时间等信息的文字颜色是黑色,搭配上状态栏蓝色的背景,好丑啊

在讲解 沉浸式体验的实现异形屏 适配前,我们需要先补下课,了解些微的基础知识:

各种Flag

STATUS_BAR_HIDDEN

/**
 * Android 3.0(API 11)加入 
 *
 * 已被废弃,现等同于 SYSTEM_UI_FLAG_LOW_PROFILE
 */
public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;

STATUS_BAR_VISIBLE

/**
 * Android 3.0(API 11)加入 
 * 
 * 已被废弃,现等同于 SYSTEM_UI_FLAG_VISIBLE
 */
public static final int STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE;

SYSTEM_UI_FLAG_VISIBLE

/**
 * Android 4.0(API 14)加入
 *
 * 表明设置系统UI(status bar等)为可见状态,
 * 这也是默认效果。
 */
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;

SYSTEM_UI_FLAG_LOW_PROFILE

/**
 * Android 4.0(API 14)加入
 *
 * 低配模式,这里的低配不是传统意义上的低配置,
 * 我更倾向于称其为低调模式,设置系统UI进入不显眼的“低配置”模式。
 * 具体表现为:状态栏 和/或 导航栏某些图标 可能变暗
 */
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;

 

SYSTEM_UI_FLAG_HIDE_NAVIGATION

/**
 * Android 4.0(API 14)加入
 * 
 * 如果设置了 SYSTEM_UI_FLAG_LOW_PROFILE 后还是觉得系统UI显眼,
 * 不妨尝试设置该Flag请求系统导航栏暂时隐藏,
 * 这将导致基本的导航控件(对于国内用户来说就是Android的三大金刚键)消失
 * 这对于充分使用设备屏幕极为有用,搭配 FLAG_LAYOUT_IN_SCREEN 食用效果更佳~
 *
 * Tip:Google 认为导航控件十分重要,因此该Flag只会暂时性隐藏导航控件
 * 用户的任何行为都会使导航栏控件重新出现,并 clear 掉该标志位
 */
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;

SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

/**
 * Android 4.1(API 16)加入
 *
 * 与 SYSTEM_UI_FLAG_HIDE_NAVIGATION 相比多了一个 LAYOUT 关键字,
 * 不同的是,使用该 Flag 并不会隐藏 system UI, 
 * 而是使用户的布局延伸至 system UI 下方,
 * 导致用户的布局被覆盖掉(可使用fitSystemWindows(Rect)方法规避该问题)
 */
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;

 

 

SYSTEM_UI_FLAG_FULLSCREEN

/**
 * Android 4.1(API 16)加入
 *
 * 全屏模式,此模式用户的内容视图将覆盖整个屏幕,
 * 这就意味着非关键的屏幕装饰(例如状态栏,不包括导航栏,Google认为导航栏是非常重要的系统UI)将被隐藏
 * 与 WindowManager.LayoutParams.FLAG_FULLSCREEN 不同的是:
 *
 * 1.如果与 Window#FEATURE_ACTION_BAR_OVERLAY 一同使用,则 ActionBar 也会一同隐藏
 * 2.该FLAG更易清除:与 系统UI交互(下拉显示通知) 或 跳转至其他应用 都会清除该FLAG
 */
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;

 

 

SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

/**
 * Android 4.1(API 16)加入
 *
 * 同 SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 与 SYSTEM_UI_FLAG_HIDE_NAVIGATION 的区别一样,
 * 该 FLAG 与 SYSTEM_UI_FLAG_FULLSCREEN 区别就是该 FLAG 会使用户布局延伸至系统状态栏下方
 * 导致用户的布局被系统状态栏覆盖掉(可使用fitSystemWindows(Rect)方法规避该问题)
 */
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;

 

 

SYSTEM_UI_FLAG_LAYOUT_STABLE

/**
 * Android 4.1(API 16)加入
 *
 * 该标志位通常与其他FLAG一同使用,表明系统期望View的insets是固定不变的
 * 这意味着即使当前 window 的 Flag 变化影响了系统UI的布局,
 * 用户界面的位置也并不会发生变化。
 *
 * 单独使用该FLAG界面不会有任何变化。
 */
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;

 

 

SYSTEM_UI_FLAG_IMMERSIVE

/**
 * Android 4.4(API 19)加入
 *
 * 该FLAG是用来修饰 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
 * 如果我们单独使用 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
 * 那么用户与系统任何交互都会清除掉 SYSTEM_UI_FLAG_HIDE_NAVIGATION 这个FLAG
 * 搭配该FLAG后则不会清除掉,除非从屏幕顶部向下滑动。
 */
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;

SYSTEM_UI_FLAG_IMMERSIVE_STICKY

/**
 * Android 4.4(API 19)加入
 *
 * 该FLAG与 SYSTEM_UI_FLAG_IMMERSIVE 不同的是
 * SYSTEM_UI_FLAG_IMMERSIVE 仅用来修饰 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
 * SYSTEM_UI_FLAG_IMMERSIVE_STICKY 是用来修饰 SYSTEM_UI_FLAG_HIDE_NAVIGATION 与
 * SYSTEM_UI_FLAG_FULLSCREEN 这两个Flag的。
 *
 * 还有一点不同的是,该FLAG在与系统UI交互后,会被短暂的clear,
 * 一会儿自动恢复。
 */
public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;

System UI 颜色

按照时间跨度来讲,Android 系统的 System UI 经历了以下几个阶段:

Android 4.4,可以设置状态栏或者导航栏的背景为透明

从Android 4.4开始,Android 系统才开始了对 System UI 颜色的改造之路,当然,Android 4.4只是小试牛刀,只允许开发者设置状态栏或者导航栏背景透明,只需要在Application的Theme里加入以下代码:

<style name="AppTheme" parent="Theme.AppCompat">
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowTranslucentNavigation">true</item>
</style>

该style在 Android 5.0 以上 和 以下的效果并不一样:

5.0以下表现为 一个渐变的半透明效果;

5.0以上取消了这个渐变效果。

Android 5.0,可以设置状态栏或者导航栏的颜色了

得益于 Google 在 Android 5.0 推出的 Material Design的设计语言,Android 开发者终于能够将状态栏和导航栏设置成任何你想要的颜色了,只需调用以下方法:

/**
 * Android 5.0(API 21)加入
 * 
 * 设置状态栏的颜色为 {@code color}。
 * 值得注意的是,该方法与前面提到的“透明状态栏”是冲突的,
 * 这意味着,如果设置了“透明状态栏”,
 * 再调用该方法无效。
 */
public abstract void setStatusBarColor(@ColorInt int color);

/**
 * Android 5.0(API 21)加入
 * 
 * 设置导航栏的颜色为 {@code color}。
 * 值得注意的是,该方法与前面提到的“透明导航栏”是冲突的,
 * 这意味着,如果设置了“透明导航栏”,
 * 再调用该方法无效。
 */
public abstract void setNavigationBarColor(@ColorInt int color);

Android 6.0,可以设置状态栏中的图标、字体颜色了

Android 中系统状态栏中的字体、图标的颜色默认为浅色系(白色),如果App状态栏设计为浅色调的话,很有可能导致用户看瞎了眼也看不清楚状态栏的各种信息。这个尴尬的点一直到Android 6.0之后才得以解决。

Android 6.0加入了更改状态栏内容颜色风格的Flag:

/**
 * Android 6.0(API 23)加入
 *
 * 设置状态栏文字图标以“Light”模式绘制,
 * 通俗的讲,就是状态栏文字图标颜色变为黑色。
 */
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;

亦或者是通过在Style中进行如下设置来达到同样的效果:

<style name="AppTheme" parent="Theme.AppCompat">
     <item name="android:windowLightStatusBar">true</item>
</style>

知道了上面这些,我们可以做...

充分了解上面介绍的 Android 各种 SystemUI 的特性后,我们就可以实现市面上大多数应用所谓的沉浸式体验了。下面我们就来看几个🌰。

页面含有复杂背景/纹理

 

 

类似于QQ音乐歌曲播放详情页面这种含有复杂的背景以及纹理的页面,市面上常见的UI设计就是设置状态栏透明,同时使用户布局侵入状态栏下方

代码如下:

	private fun translucent(){
        supportActionBar?.hide()
        val decorView =  window?.decorView
        decorView?.systemUiVisibility = 
  					View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 
        window?.statusBarColor = Color.TRANSPARENT
    }

效果如下:

用户布局顶部为纯色背景

 

 

而像支付宝这种用户布局顶部为纯色背景的设计,常见思路就是将statusbar的颜色设为同色且用户布局不侵占状态栏即可。

代码如下:

	private fun translucent(){
        supportActionBar?.hide()
        window?.statusBarColor = Color.TRANSPARENT
    }

设置完状态栏颜色后,不要忘记根据状态栏的颜色深浅设置状态栏图标对应的深色/浅色模式:
decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR状态栏浅色模式:对应的状态栏图标变为深色(黑色),不设置即为深色模式,对应的状态栏图标为浅色(白色)。

类电子书阅读器

类似于微信阅读这种,用户希望在阅读电子书籍时能够全情投入不受系统状态栏信息变化的影响同时又能够随时呼出系统状态栏以及App的菜单。like this:

 

这种效果的实现很显然就是要动态更改decorViewsystemUiVisibility了。如果你对上面介绍的各种system ui flag了然于胸的话,一定能够不加思索的写出下面的代码:

   /**
     * 根据传入的visible参数动态改变decorView的systemUiVisibility
     * 
     * @param 是否展示 status bar
     */
    private fun changeSystemUI(visible: Boolean){
        var newVis = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
        llActionBar.visibility = View.VISIBLE
        if (!visible) {
            newVis =
                newVis or (ImageView.SYSTEM_UI_FLAG_LOW_PROFILE or ImageView.SYSTEM_UI_FLAG_FULLSCREEN
                        or ImageView.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
            llActionBar.visibility = View.GONE
        }
        val decorView = window?.decorView
        decorView?.systemUiVisibility = newVis
    }

效果如下:

 

总结

这篇只是简单介绍了下Android View体系中的system ui的基础,关于Android P中的挖孔屏(notch)相关适配知识以及国内种类繁多的定制room系统(MIUI、EMOTION UI、Fly Me...)的system ui以及挖孔屏的适配,受制于篇幅,暂时就讲这么多了,下方有福利领取;

福利群领取:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章