转自:http://blog.csdn.net/jdsjlzx/article/details/41643587

Android 沉浸式状态栏、状态栏一体化、透明状态栏、仿iOS透明状态栏

http://blog.csdn.net/jdsjlzx/article/details/50437779

注:状态栏的字体颜色位白色, 如果状态栏背景为白色,上面的博客已经解决了。

原文出处:http://niorgai.github.io/2016/03/20/Android-transulcent-status-bar/

最近业务上看到一个设计图挺好看,所以研究了一下透明状态栏,注意不是沉浸式状态栏,在参考了网上的一些资料后,整理出了这篇博客.

Github Demo 链接: StatusBarCompat

参考文章:

  1. 由沉浸式状态栏引发的血案

  2. Translucent System Bar 的最佳实践

  3. 该使用 fitsSystemWindows 了!


首先强调,对于状态栏的处理有两种不同的方式, 这里从Translucent System Bar 的最佳实践直接盗了两张图做对比~.

全屏( ContentView 可以进入状态栏) 非全屏 ( ContentView 与状态栏分离, 状态栏直接着色)

先定义几个名词:

  1. 全屏模式: 左边图所示.

  2. 着色模式: 右边图所示.

  3. ContentView: activity.findViewById(Window.ID_ANDROID_CONTENT) 获取的 View , 即 setContentView 方法所设置的 View, 实质为 FrameLayout.

  4. ContentParent: ContentView 的 parent , 实质为 LinearLayout.

  5. ChildView: ContentView 的第一个子 View ,即布局文件中的 layout .

再介绍一下相关的函数:

  1. fitsSystemWindows, 该属性可以设置是否为系统 View 预留出空间, 当设置为 true 时,会预留出状态栏的空间.

  2. ContentView, 实质为 ContentFrameLayout, 但是重写了 dispatchFitSystemWindows 方法, 所以对其设置 fitsSystemWindows 无效.

  3. ContentParent, 实质为 FitWindowsLinearLayout, 里面第一个 View 是 ViewStubCompat, 如果主题没有设置 title ,它就不会 inflate .第二个 View 就是 ContentView.

5.0以上的处理:

自5.0引入 Material Design ,状态栏对开发者更加直接,可以直接调用 setStatusBarColor 来设置状态栏的颜色.

全屏模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. //设置透明状态栏,这样才能让 ContentView 向上
  3. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  4. //需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
  5. window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  6. //设置状态栏颜色
  7. window.setStatusBarColor(statusColor);
  8. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  9. View mChildView = mContentView.getChildAt(0);
  10. if (mChildView != null) {
  11. //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 使其不为系统 View 预留空间.
  12. ViewCompat.setFitsSystemWindows(mChildView, false);
  13. }

着色模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. //取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
  3. window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  4. //需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
  5. window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  6. //设置状态栏颜色
  7. window.setStatusBarColor(statusColor);
  8. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  9. View mChildView = mContentView.getChildAt(0);
  10. if (mChildView != null) {
  11. //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 预留出系统 View 的空间.
  12. ViewCompat.setFitsSystemWindows(mChildView, true);
  13. }

4.4-5.0的处理:

4.4-5.0因为没有直接的 API 可以调用,需要自己兼容处理,网上的解决方法基本都是创建一下高度为状态栏的 View ,通过设置这个 View 的背景色来模拟状态栏. 这里我尝试了三种方法来兼容处理.

方法1: 向 ContentView 添加假 View , 设置 ChildView 的 marginTop 属性来模拟 fitsSystemWindows .

全屏模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  3. //首先使 ChildView 不预留空间
  4. View mChildView = mContentView.getChildAt(0);
  5. if (mChildView != null) {
  6. ViewCompat.setFitsSystemWindows(mChildView, false);
  7. }
  8. int statusBarHeight = getStatusBarHeight(activity);
  9. //需要设置这个 flag 才能设置状态栏
  10. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  11. //避免多次调用该方法时,多次移除了 View
  12. if (mChildView != null && mChildView.getLayoutParams() != null && mChildView.getLayoutParams().height == statusBarHeight) {
  13. //移除假的 View.
  14. mContentView.removeView(mChildView);
  15. mChildView = mContentView.getChildAt(0);
  16. }
  17. if (mChildView != null) {
  18. FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams();
  19. //清除 ChildView 的 marginTop 属性
  20. if (lp != null && lp.topMargin >= statusBarHeight) {
  21. lp.topMargin -= statusBarHeight;
  22. mChildView.setLayoutParams(lp);
  23. }
  24. }

着色模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  3. //First translucent status bar.
  4. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  5. int statusBarHeight = getStatusBarHeight(activity);
  6. View mChildView = mContentView.getChildAt(0);
  7. if (mChildView != null) {
  8. FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams();
  9. //如果已经为 ChildView 设置过了 marginTop, 再次调用时直接跳过
  10. if (lp != null && lp.topMargin < statusBarHeight && lp.height != statusBarHeight) {
  11. //不预留系统空间
  12. ViewCompat.setFitsSystemWindows(mChildView, false);
  13. lp.topMargin += statusBarHeight;
  14. mChildView.setLayoutParams(lp);
  15. }
  16. }
  17. View statusBarView = mContentView.getChildAt(0);
  18. if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == statusBarHeight) {
  19. //避免重复调用时多次添加 View
  20. statusBarView.setBackgroundColor(statusColor);
  21. return;
  22. }
  23. statusBarView = new View(activity);
  24. ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
  25. statusBarView.setBackgroundColor(statusColor);
  26. //向 ContentView 中添加假 View
  27. mContentView.addView(statusBarView, 0, lp);

方法2: 向 ContentParent 添加假 View ,设置 ContentView 和 ChildView 的 fitsSystemWindows.

全屏模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  3. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  4. ViewGroup mContentParent = (ViewGroup) mContentView.getParent();
  5. View statusBarView = mContentParent.getChildAt(0);
  6. if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
  7. //移除假的 View
  8. mContentParent.removeView(statusBarView);
  9. }
  10. //ContentView 不预留空间
  11. if (mContentParent.getChildAt(0) != null) {
  12. ViewCompat.setFitsSystemWindows(mContentParent.getChildAt(0), false);
  13. }
  14. //ChildView 不预留空间
  15. View mChildView = mContentView.getChildAt(0);
  16. if (mChildView != null) {
  17. ViewCompat.setFitsSystemWindows(mChildView, false);
  18. }

着色模式(会有一条黑线,无法解决):

[js] view plain copy
  1. Window window = activity.getWindow();
  2. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  3. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  4. ViewGroup mContentParent = (ViewGroup) mContentView.getParent();
  5. View statusBarView = mContentParent.getChildAt(0);
  6. if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
  7. //避免重复调用时多次添加 View
  8. statusBarView.setBackgroundColor(statusColor);
  9. return;
  10. }
  11. //创建一个假的 View, 并添加到 ContentParent
  12. statusBarView = new View(activity);
  13. ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  14. getStatusBarHeight(activity));
  15. statusBarView.setBackgroundColor(statusColor);
  16. mContentParent.addView(statusBarView, 0, lp);
  17. //ChildView 不需要预留系统空间
  18. View mChildView = mContentView.getChildAt(0);
  19. if (mChildView != null) {
  20. ViewCompat.setFitsSystemWindows(mChildView, false);
  21. }

方法3:向 ContentView 添加假 View , 设置 ChildView 的 fitsSystemWindows.

全屏模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  3. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  4. View statusBarView = mContentView.getChildAt(0);
  5. //移除假的 View
  6. if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) {
  7. mContentView.removeView(statusBarView);
  8. }
  9. //不预留空间
  10. if (mContentView.getChildAt(0) != null) {
  11. ViewCompat.setFitsSystemWindows(mContentView.getChildAt(0), false);
  12. }

着色模式:

[js] view plain copy
  1. Window window = activity.getWindow();
  2. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  3. ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
  4. int statusBarHeight = getStatusBarHeight(activity);
  5. View mTopView = mContentView.getChildAt(0);
  6. if (mTopView != null && mTopView.getLayoutParams() != null && mTopView.getLayoutParams().height == statusBarHeight) {
  7. //避免重复添加 View
  8. mTopView.setBackgroundColor(statusColor);
  9. return;
  10. }
  11. //使 ChildView 预留空间
  12. if (mTopView != null) {
  13. ViewCompat.setFitsSystemWindows(mTopView, true);
  14. }
  15. //添加假 View
  16. mTopView = new View(activity);
  17. ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
  18. mTopView.setBackgroundColor(statusColor);
  19. mContentView.addView(mTopView, 0, lp);

其实全屏模式在三种模式下实现都是一样的,主要是着色模式实现不同.

对比一下三种着色模式实现的方式:

方法1 方法2 方法3
原理 向 ContentView 中添加假 View, 然后利用 ChildView 的 marginTop 属性来模拟 fitsSystemWindows ,主要是通过修改 marginTop 的值可以在全屏模式和着色模式之间切换. 因为 ParentView 的实质是一个 LinearLayout , 可以再其顶部添加 View . 向 ContentView 中添加假 View, 然后利用ChildView 的 fitsSystemWindows 属性来控制位置, 但是实现缺陷就是不能随时切换两种模式.
缺陷 改变了 ChildView 的 marginTop 值 着色模式下,会像由沉浸式状态栏引发的血案中一样出现一条黑线 不能在不重启 Activity 的情况下切换模式.
对应 Github demo 中代码 StatusBarCompat类 StatusBarCompat1类 StatusBarCompat2 类

总结

  • StatusBarCompat2 主要问题不能切换.

  • StatusBarCompat1 在4.4上会有一条黑线, 如果可以解决我觉得这是最靠谱的解决方法.

  • StatusBarCompat 类算是我最后给出的解决方案吧, 目前使用效果比较完善.推荐使用

    • 用户可以随时在同一个 Activity 中切换不同的状态栏模式.

    • 就算子 View 重写了 dispatchFitSystemWindows 也不会有影响.

推荐干货集中营Android客户端,实现了沉浸式状态栏,无缝换肤,带3D感觉的侧滑菜单

主页及侧滑页截图

gank1.png

福利及换肤截图

【Android】关于statusbar的处理相关推荐

  1. android 状态栏(StatusBar)

    一.SystemUI 概述 自 android2.2 开始 , 原本存在与 framework-res.apk 中的状态栏和下拉通知栏界面控制被分割出一个单独的 apk文件 , 命名为 SystemU ...

  2. Android FrameWork——StatusBar

    Android系统顶上的状态栏是属于FrameWork的东东,由于项目上需要对状态栏进行一些修改调整,我对其作了一个初步研究,写出来大家共享一下,其实这些早已写了,只是想等研究StatusBar中Ex ...

  3. android 隐藏statusbar,Android状态栏StatusBar常见用法

    什么是StatusBar 首先,谷歌官方的解释是,用于显示通知,传达设备状态的屏幕区域,与应用同时显示.而为了能够使应用显示沉浸式的内容(如电影图片等),官方赋予了应用对状态栏在一定程度上的控制能力, ...

  4. android 隐藏statusbar,Android StatusBar(状态栏) 显示、隐藏、沉浸式、颜色修改

    一.StatusBar颜色修稿 public static void setStatusBarColor(@NonNull Window window, @ColorInt int color, bo ...

  5. Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

    前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...

  6. Android 9.0系统源码_SystemUI(二)StatusBar系统状态栏的创建流程

    前言 上一篇我们具体分析了SystemUI的启动流程,在SystemServer的startOtherServices方法中,会启动SystemUIService服务,SystemUIService服 ...

  7. Android P SystemUI之StatusBar UI布局status_bar.xml解析

    相关源码: \frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\CollapsedStatusBar ...

  8. android动态显示/隐藏statusbar,android4.4动态显示隐藏statusbar和禁止下拉

    android4.4增加动态显示隐藏statusbar和禁止statusbar下拉功能 首先还是在设置->显示里加上图形界面 修改packages/apps/Settings/res/xml/d ...

  9. Android SystemUI之StatusBar,状态栏(二)

    Android  SystemUI系列: 1.Android  SystemUI之启动流程(一) 2.Android SystemUI之StatusBar,状态栏(二) 3.Android Syste ...

  10. Android 12系统源码_SystemUI(二)系统状态栏StatusBar的创建流程

    前言 上一篇我们具体分析了SystemUI的启动流程,在SystemServer的startOtherServices方法中,会启动SystemUIService服务,SystemUIService服 ...

最新文章

  1. Mac之当前目录打开终端
  2. GIS二次开发之初探
  3. [转载]使用awk进行数字计算,保留指定位小数
  4. Matlab中图例注释函数legend详解
  5. Spring:笔记整理(1)——HelloWorld
  6. [已解决] 日常开发中禁用Tomcat自动重启
  7. 不一样的三一重工:重在选择 —— 三一集团CIO潘睿刚专访系列之一
  8. Machine Learning Lecture Notes
  9. 用C语言实现:打印100-200之间的素数。
  10. 不是我发现了美,只不过这个世界本身就很美。记快乐的今年生日。
  11. python opencv读取图像并生成plt文件
  12. PlatformIO开发STM8S003F3P6
  13. 同学聚会幽默 , 小学生搞笑图片
  14. win10自带微软拼音输入法卡死卡顿解决方法汇总
  15. bat的使用技巧汇总
  16. 金山也推隐私保护器,我的隐私谁做主?
  17. 11.21的自动锁屏 ios_iPhone11 的新手小技巧
  18. AssertionError: View function mapping is overwriting an existing endpoint function: inner
  19. hadoop yarn web无法访问
  20. Json简介与基本使用

热门文章

  1. jQuery-file-upload插件的使用(小实例)
  2. DJ1-2 操作系统引论
  3. html合并单元格怎么把字竖着,电脑excel单元格中文字如何在合并单元格后竖排显示...
  4. Python画了一朵花
  5. 电脑蓝屏---错误代码:0xc0000185,修复过程转0xc0000001,最后成功修复
  6. PCA(非常详细)【机器学习】
  7. 机器学习算法——支持向量机SVM5(核函数)
  8. 谱分析——傅里叶级数(离散谱)
  9. Windows系统安装搭建ios开发环境
  10. 一次性奖金是否选择并入综合所得测算表