# SystembarSample **Repository Path**: liu_wanshun/SystembarSample ## Basic Information - **Project Name**: SystembarSample - **Description**: Android 系统栏UI统一规范 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-27 - **Last Updated**: 2022-05-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: Android ## README # Android 系统栏UI统一规范 ## 1.改变系统栏的颜色 **Android 10** 在 Android 10 上,我们只需要将系统栏颜色设为完全透明即可: ```xml ``` 在 Android 10 上,系统会负责在所有导航模式下确保系统栏的内容可见 (包括时间、图标、拖拽条等)。这意味着这些事情不再需要我们操心。具体来说,系统会执行以下两项操作之一,这两种操作都是为了确保用户始终可以看到系统栏的内容。系统选择采用哪种做法取决于多个因素。 1. 动态颜色适配 ![](https://mmbiz.qpic.cn/mmbiz_gif/rFWVXwibLGtxaEO2wX347hQ9jMzRtDVAUFZOibbndficcHibzI3SkVpmGAtMp3D4jibnVicRZa9oIGv8hm9Lb0bqOGFQ/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1) 2. 半透明遮盖(targetSdkVersion >= 29) 如果您的应用针对的是 SDK 28 或更低版本,则系统不会显示遮盖,而是提供透明的导航栏。 如果满足以下条件,系统将使用遮盖: - 启用了两键或三键导航模式。 ![](https://mmbiz.qpic.cn/mmbiz_png/rFWVXwibLGtxaEO2wX347hQ9jMzRtDVAU2M2CXrl1h7CFkohb8lDJDUQty8Xu5O9ypV7ibPBVm5Mp8PgSuh7ibjJg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) - 设备制造商在手势导航模式下禁用了动态颜色适配。制造商这么做的原因可能是设备的性能不足以支持动态色彩适配。 ![](https://mmbiz.qpic.cn/mmbiz_png/rFWVXwibLGtxaEO2wX347hQ9jMzRtDVAU4kN8wqriaFwlSRr6cv6jp243OT1lkQSAicXkqaXYQU2ibnp8pgmoMiakZw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) *在 Android 10 上禁用系统栏视觉保护* 如果您不想让系统执行任何自动内容视觉保护,则可以通过在主题中将 android:enforceNavigationBarContrast 和/或 android:enforceStatusBarContrast 的值设置为 false。此时将提供透明的导航栏。 **Android 9 及更早版本** 1. Android 8 (api 26)可以使用透明状态栏,导航栏,然后在代码中手动**修改状态栏文字颜色,导航栏按钮颜色,防止文字/按钮看不清**。 ```kotlin //设置亮色状态栏(状态栏图标暗色)仅在APi >= 23 时有效 ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = true //设置暗色状态栏(状态栏图标亮色)仅在APi >= 23 时有效 ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = false //设置亮色导航栏(导航栏图标暗色)仅在APi >= 26 时有效 ViewCompat.getWindowInsetsController(view)?.isAppearanceLightNavigationBars = true //设置暗色状态栏(状态栏图标亮色)仅在APi >= 26 时有效 ViewCompat.getWindowInsetsController(view)?.isAppearanceLightNavigationBars = false ``` 2. Android 6 及以上(api >= 23)可以透明状态栏,导航栏保持默认。 3. Android 6 以下(api < 23)状态栏、导航栏均保持默认。 ## 2.开启全面屏幕体验 ```kotlin //通知视窗,我们(应用)会处理任何系统视窗(而不是 decor) 需要androidx.core 库的 v1.5.0以上版本 WindowCompat.setDecorFitsSystemWindows(window, false) ``` ![](https://mmbiz.qpic.cn/mmbiz_gif/rFWVXwibLGtxaEO2wX347hQ9jMzRtDVAUz7HkULu2Ds3934Gwp5q4hT39V2zSeuLGhg20jWDmWJWLOWXhF24rBg/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1) ## 3.**处理视觉冲突** 避免与系统 UI 产生重叠,也可以说是使用视窗边衬区来决定如何移动应用的内容来避免与系统 UI 的冲突。 ```kotlin ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> val sysWindow = insets.getInsets(Type.systemBars() or Type.ime()) val stable = insets.getInsetsIgnoringVisibility(Type.systemBars()) val systemGestures = insets.getInsets(Type.systemGestures()) val tappableElement = insets.getInsets(Type.tappableElement()) //给View设置bottompaddding 等于系统导航栏高度,防止View与导航栏重叠 v.updatePadding(bottom = sysWindow.bottom) //还可以获取软键盘的可见性,高度,参考:https://mp.weixin.qq.com/s/U_c1l25kSUwnVLcIWYVBcQ val imeVisible = insets.isVisible(Type.ime()) val imeHeight = insets.getInsets(Type.ime()).bottom // 返回边衬区,这样它们才能够继续在视图树中继续传递下去 insets } ``` ## 4.推荐方案 ### 1.主题 通过上面的了解,可以发现,系统栏的适配较为复杂,因为不同安卓版本的支持不一样,需要区分对待。 但是当前app绝大多数界面背景相对统一,比如在非夜间模式下,app背景绝大多数为亮色(偏白色);而在深色模式下,app背景绝大多数为深色(偏黑色)。 根据这个特征,因此我们可以根据背景,在主题中统一适配,减少代码书写。 ```xml ``` ```xml ``` ```xml ``` 以上是基本主题,作为parent。接下来定义AppTheme,使用这个AppTheme作为主题 ```xml ``` ### 2.根据亮色/深色主题设置系统栏图标颜色 设置系统栏亮色模式与主题一致(系统栏亮色模式则图标深色;系统深色模式则图标亮色) ```kotlin class App : Application() { override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(SystemBarActivityLifecycleCallbacks) } } object SystemBarActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks { private val lightThemeAttributes = intArrayOf(R.attr.isLightTheme) override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { val lightThemeTypedArray: TypedArray = activity.obtainStyledAttributes(lightThemeAttributes) //判断当前是否是亮色主题 val isLightTheme = lightThemeTypedArray.getBoolean(0, true) lightThemeTypedArray.recycle() ViewCompat.getWindowInsetsController(activity.findViewById(android.R.id.content))?.apply { isAppearanceLightStatusBars = isLightTheme isAppearanceLightNavigationBars = isLightTheme } } ...... } ``` 此方案适配绝大多数UI界面情况。对于这种方案无法适配到的,可在具体界面的代码中进行微调。 ### 3.间距 当我们使用开启全面时,有些UI元素可能会显示到系统栏位置,与系统栏互相遮挡。 我们想要使部分UI元素不要显示在系统栏所在到位置,我们可以使用上面的第3点处理视觉冲突 ```kotlin ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> val sysWindow = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.updatePadding(top = sysWindow.top) insets } ``` 但是当有多个UI元素需要这样处理时,可能会形成大量模版代码。 因此可以尝试使用这个库 **Insetter**(https://gitee.com/liu_wanshun/Insetter),免除这种模版代码的书写。 ## 5.推荐阅读: 1.实现边到边的体验 | 让您的软键盘动起来 (一) https://mp.weixin.qq.com/s/U_c1l25kSUwnVLcIWYVBcQ 2.开启全面屏体验 | 手势导航 (一) https://mp.weixin.qq.com/s/DEI4bcmKkRBySUjO2AYEJA 3.处理视觉冲突 | 手势导航 (二) https://mp.weixin.qq.com/s/p_9Px7BH6DQGNYNqzPCWtw 4.再学一遍android:fitsSystemWindows属性 https://mp.weixin.qq.com/s/AiCNJAi9CgYDE1UuzCboGg