Day20-沉浸式状态栏

概念

  1. 全屏模式
  2. 变色状态栏模式
  3. 背景全屏+变色状态栏模式
  4. 图片全屏+变色状态栏模式
  5. ContentView: activity.findViewById(Window.ID_ANDROID_CONTENT) 获取的 View , 即 setContentView 方法所设置的 View, 实质为 FrameLayout.
  6. ContentParent: ContentView 的 parent , 实质为 LinearLayout.
  7. ChildView: ContentView 的第一个子 View ,即布局文件中的 layout.

相关函数

  1. fitsSystemWindows, 该属性可以设置是否为系统 View 预留出空间, 当设置为 true 时,会预留出状态栏的空间.
  2. ContentView, 实质为 ContentFrameLayout, 但是它的 dispatchFitSystemWindows 重写了 fitsSystemWindows 方法, 所以对其设置 fitsSystemWindows 无效.
  3. ContentParent, 实质为 FitWindowsLinearLayout, 里面第一个 View 是 ViewStubCompat, 如果主题没有设置 title ,它就不会 inflate .第二个 View 就是 ContentView.
  4. setClipToPadding: 保持padding, true为保持

概述

  • 先看一下四张图
  • 4.4.4默认
    Day20-沉浸式状态栏_第1张图片
  • 4.4.4 去掉 ActionBar
    Day20-沉浸式状态栏_第2张图片
  • 5.0 默认
    Day20-沉浸式状态栏_第3张图片
  • 5.0去掉 ActionBar
    Day20-沉浸式状态栏_第4张图片
  • DecorView结构:
    --DecorView == extends FrameLayout
    ------View (4.4在这自定义个伪状态栏)
    ------LinearLayout
    ----------ViewStub
    ----------FrameLayout
    --------------FitWindowsLinearLayout(style设置了不带toolbar)
    ------------------ViewStubCompat
    ------------------ContentFrameLayout == ContentView
    --------------ActionBarOverlayLayout(style设置了带toolbar)
    ------------------ContentFrameLayout == ContentView
    ------------------ActionBarContainer
    ------view(5.0才有的statusbarbackground, 系统级, getChildAt找不到,得finsViewById找)
    ------view(5.0才有的navigationbackground, 系统级, getChildAt找不到,得finsViewById找)
总结一下:
可以看出 DecorView 作为根布局, 持有一个 LinearLayout,
* 5.0在LinearLayout下持有四个View, 两个系统级的 View(StatusBar 和 Navigation), 和两个 FrameLayout, 一个是ViewStub, 一个是给了 ContentFraneLayout + ActionBar(当设置不需要 ActionBar 时, 是ViewStubCompat).
* 4.4在LinearLayout下只有两个View, 可以推出5.0可以直接```setStatusBarColor```是因为多了这俩view, 那我们只要在 LinearLayout 中插入一个 view 就可以学着5.0的样子, 让ContentFraneLayout下移.

4.4 以下放弃

4.4.4 kitcat

谷歌在 Android 的 Window 属性里添加了可以让状态栏和导航栏被穿透(ContentView覆盖StatusBar)的属性

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
  • 建议使用代码设置(国产ROM有迷之bug)
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

全屏模式

  1. 给 Window 添加 FLAG_TRANSLUCENT_STATUS
  2. 给 ContentView 的子 view 设置 setFitsSystemWindows

FLAG_TRANSLUCENT_STATUS 效果:
自动设置了 SYSTEM_UI_FLAG_LAYOUT_STABLE 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 让 ContentView 满屏

变色状态栏模式

  1. 给 Window 添加 FLAG_TRANSLUCENT_STATUS
  2. 给 DecorView 添加一个伪状态栏
  3. 给 ContentView 的子 view 全部设置 setFitsSystemWindows

背景全屏+变色状态栏模式

  1. 给 Window 添加 FLAG_TRANSLUCENT_STATUS
  2. 给 ContentView 的子 view 全部设置 fitsSystemWindows, 空出StatusBar的高给伪状态栏
  3. 给 ContentView 添加一个伪 StatusBar

图片全屏+变色状态栏模式

  1. 给 Window 添加 FLAG_TRANSLUCENT_STATUS
  2. 给 ContentView 添加一个伪 StatusBar
  3. 如果是 Imageview 浮在布局上, 即下一个布局也怼上了 StatusBar, 需要手动给后续布局加 topMargin

5.0 LOLLIPOP

比起 4.4, 不需要 FLAG_TRANSLUCENT_STATUS, 因为谷歌又�提供了setStatusBarColor, 不过默认 StatusBar 的会添加半透明灰条, 所以还得改下 StatusBar 的颜色为透明

全屏模式:

  1. 给 Window 设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 让 ContentView 怼到顶
  2. 给 Window 设置setStatusBarColor 修改透明颜色

变色状态栏模式

  1. 给 Window 设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 让 ContentView 怼到顶
  2. 给 windiw 设置setStatusBarColor 修改指定颜色

背景全屏+变色状态栏模式

  1. 给 Window 设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 让 ContentView 怼到顶
  2. 给 Window 设置setStatusBarColor 修改透明颜色
  3. 给 ContentView 的子 view 全部设置 fitsSystemWindows, 空出StatusBar的高给伪状态栏
  4. 给 ContentView 添加伪 StatusBar

图片全屏+变色状态栏模式(3,4步和4.4处理方式一样)

  1. 给 StatusBar 设置透明
  2. 给 DecorView 设置 SYSTEM_UI_FLAG_LAYOUT_STABLE 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

是不是很眼熟, 对, 就是FLAG_TRANSLUCENT_STATUS自动附带的设置, 不过5.0 可以直接改 StatusBar 的颜色了, 不需要 TRANSLUCENT_STATUS

  1. 给 ContentView 添加伪 StatusBar
  2. 如果是 Imageview 浮在布局上, 即下一个布局也怼上了 StatusBar, 需要手动给后续布局加 topMargin

特殊需求:

如果一个 StatusBar 可变色的 acitivy, 加入图片全屏 + 变色StatusBar 的 fragment/或 StatusBar 联动的 fragment 也就是当 StatusBar 和 fragment 一体, 建议�此 fragment 替换为 activity, 虽然可以�实现, 但是需要页面绘制两遍不说, 二是 StatusBar 是在页面出现后加入的. 如果项目一开始就有这个需求, 那么在全部fragment�的xml中加状态栏高的view

fragment中的沉浸式

思路:
如果需要 StatusBar 和 fragment 一起联动, 需要多两步

  1. 在fragment的xml中添加一个view(StatusBar_view_height).
  2. xml中设置StatusBar_view_height的值
 - 0dp
v19 - 25dp
vw820dp - 64dp

抽屉

和activity一个处理思路

实现

Tips:
自定义Theme 运行时是先找自己版本号对应的styles, 如果没有那就去找无版本号的styles里的. 所以一定要在无版本号的styles里写上自定义Theme, 哪怕里面没有东西, 不然直接崩
推荐通过代码修改, 单纯的依靠theme也可以, 但是不如代码直观, 也不便于控制

1. theme中需要做的铺垫

a. 通过代码里设置

  1. 去掉自带actionBar
    false
    false
    true
    
  2. 去掉打开时先加载一闪而过的 空白背景+默认StatusBar 的默认背景
    true
    @android:color/transparent
    

2. 代码:

https://github.com/laobie/StatusBarUtil

踩坑

  • fitsSystemWindows
    到底在哪儿设置
    • 在 theme 中设置是对整个 Window, 导致Toast文字内容 上浮, 建议在代码里设置
    • 当前布局的最外层和 SystemWindow 接触, 所以给最外层的 view 设置 fitsSystemWindows 才能有效果
  • WindowTranslucentStatus
    到底设置true还是false
    true
    true
    
    @android:color/transparent
    
    • 华为7.0在theme被设置成这样时, StatusBar被摸过后就半透明的挂在头上了, 模拟器则是能直接看到半透明的StatusBar
    false
    true
    
    @android:color/transparent
    
    • 华为7.0在theme被设置成这样时, StatusBar怎么摸都没事儿, 模拟器也是看不见StatusBar的

TODO

  • flyme/miui可能的bug

参考

  • Android App 沉浸式状态栏解决方案
  • 知乎 | Android 5.0 如何实现将布局的内容延伸到状态栏?
  • 工具 | 有源代码时直接查看运行后的布局
  • 别人的整理 | 由沉浸式状态栏引发的血案
  • 别人的总结 Android-transulcent-status-bar

你可能感兴趣的:(Day20-沉浸式状态栏)