Android-transulcent-status-bar
透明状态栏适配.主要适用场景为一个Activity多个tab在不同状态栏模式下切换.
Github Demo 链接: StatusBarCompat
参考文章:
- 由沉浸式状态栏引发的血案
- Translucent System Bar 的最佳实践
- 该使用 fitsSystemWindows 了!
- Android 透明状态栏实现方案
- 更简单更全的material design状态栏
首先强调,对于状态栏的处理有两种不同的方式, 这里从Translucent System Bar 的最佳实践直接盗了两张图做对比~.
| 全屏( ContentView 可以进入状态栏) | 非全屏 ( ContentView 与状态栏分离, 状态栏直接着色) |
|---|---|
![]() |
![]() |
先定义几个名词:
- 全屏模式: 左边图所示.
- 着色模式: 右边图所示.
- ContentView:
activity.findViewById(Window.ID_ANDROID_CONTENT)获取的 View , 即setContentView方法所设置的 View, 实质为FrameLayout. - ContentParent:
ContentView的 parent , 实质为LinearLayout. - ChildView:
ContentView的第一个子 View ,即布局文件中的 root layout .
再介绍一下相关的函数:
fitsSystemWindows, 该属性可以设置是否为系统 View 预留出空间, 当设置为 true 时,会预留出状态栏的空间.ContentView, 实质为ContentFrameLayout, 但是重写了dispatchFitSystemWindows方法, 所以对其设置fitsSystemWindows无效.ContentParent, 实质为FitWindowsLinearLayout, 里面第一个 View 是ViewStubCompat, 如果主题没有设置 title ,它就不会 inflate .第二个 View 就是ContentView.requestApplyInsets(), 当窗口(Window)大小改变了,通知 View 去消费窗口的改变.FLAG_TRANSLUCENT_STATUS, 设置全屏的标志位, 此时界面可以延伸到状态栏.
5.0以上的处理:
自5.0引入 Material Design ,状态栏对开发者更加直接,可以直接调用 setStatusBarColor 来设置状态栏的颜色.
**着色模式: **
通过查看 setStatusBarColor() 方法的文档,发现在调用该方法时需要设置以下属性:
- 添加
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSFlag(绘制系统栏). - 清除
FLAG_TRANSLUCENT_STATUSFlag(透明状态栏). - 调用
setStatusBarColor()设置状态栏颜色.
**全屏模式: **
由于 5.0 以上为状态栏添加了一个阴影, 所以为全屏模式添加了是否隐藏状态栏阴影的方法.
隐藏阴影
- 像着色模式一样添加 flag ,然后通过
setStatusBarColor()设置颜色为透明. - 通过
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)隐藏状态栏颜色.
- 像着色模式一样添加 flag ,然后通过
显示阴影
- 设置
FLAG_TRANSLUCENT_STATUS来隐藏状态栏. - 通过
setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE)来恢复默认状态栏样式.
- 设置
4.4-5.0的处理:
4.4-5.0因为没有直接的 API 可以调用,需要自己兼容处理.参考了网上的解决方法及结合我自己遇到的坑,最后想出的解决办法如下:
着色模式
- 向
DecorView中添加一个 View, 高度为状态栏的高度(反射获取). - 将
ChildView的 marginTop 加上状态栏的高度,以此来模拟fitsSystemWindows. - 设置
ChildView的 fitsSystemWindow 为 false, 不预留系统栏位置. - 为
DecorView设置一个 tag, 防止重复添加 View.
这里与其他地方不同的是:
- 向
ContentView添加 View 在部分机型(华为)上没有效果. - 向
ContentParent上添加 View 会有一条黑线. - 使用 marginTop 而不是
fitsSystemWindows是因为无法在**不重启 Activity **的情况下切换 root layout 的fitsSystemWindows属性, 即直接设置不会生效, 所以用 marginTop 来模拟.
全屏模式
- 设置
ChildView的 fitsSystemWindow 为 false, 不预留系统栏位置. - 如果在
ChildView的 marginTop 中添加了状态栏的高度, 则移除. - 设置 tag, 防止重复移除.
** CollaspingToolbarLayout **
这个 support 包中的控件, 由于重写了 onApplySystemInsets() 方法, 按照我所理解的状态栏模式, 它在滑动时在两种模式中切换, 对此我的兼容方法就是让其处于 着色模式 下,在滑动时保持状态栏颜色不变. 当然有更好的解决办法, 但我这里为了方便调用(只需要传递 Activity 对象), 就用了比较简单的处理方法.
CollapsingToolbarLayout
support 包中提供的 CollapsingToolbarLayout, 在使用的时候大概的布局是这样的(参考 CheeseSquare ):
<CoordinatorLayout
android:fitsSystemWindows="true">
<AppBarLayout
android:fitsSystemWindows="true">
<CollapsingToolbarLayout
android:fitsSystemWindows="true">
<View
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"/>
<ToolBar
app:layout_collapseMode="pin"/>
</CollapsingToolbarLayout>
<AppBarLayout/>
<View
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</CoordinatorLayout>
在适配的时候主要遇到以下几个问题:
- 如果按照我上面介绍的方式来说, 它在显示图片的时候是
全屏模式, 在显示Toolbar的时候是着色模式. CollapsingToolbarLayout的内部在 5.0 及以上的版本中通过OnApplyWindowInsetsListener()中获取的 Insets 对象, 在 Layout 的过程中将 View 向下偏移了, 所以它在 5.0 及以上系统中可以占据状态栏, 在 4.4 系统上则不能.- 仿照 CheeseSquare 这个库中的写法,
CollapsingToolbarLayout需要设置FitsSystemWindow为 true, 而我在前面兼容全屏模式和着色模式的时候, 都是设置FitsSystemWindow为 false. 同时目前我没找到方法在不重启 Activity 的情况下切换FitsSystemWindow.
在参考了网上的文章后,最后决定通过以下方法来处理.
4.4版本:
- 变为全屏模式,设置 View 的
fitsSystemWindow为 false, 这一步可以使CollapsingToolbarLayout占据状态栏,Toolbar也是. - 改变
Toolbar的高度, 加上状态栏的高度, 让Toolbar挡住状态栏位置, 同时为Toolbar添加 paddingTop , 这样就可以让 title 正常显示. - 添加假的
StatusView模拟状态栏颜色, 通过AppBarLayout的OnOffsetChangedListener()监听AppBarLayout的滑动, 使StatusView跟随CollapsingToolbarLayout的显示隐藏.
- 变为全屏模式,设置 View 的
5.0及以上版本:
- 变为全屏模式,设置 View 的
fitsSystemWindow为 false, 这一步可以使CollapsingToolbarLayout占据状态栏,Toolbar也是. - 改变
Toolbar的高度, 加上状态栏的高度, 让Toolbar挡住状态栏位置, 同时为Toolbar添加 paddingTop , 这样就可以让 title 正常显示. 这都和 4.4 一样. - 为
CollapsingToolbarLayout设置OnApplyWindowInsetsListener()使其正常 Layout ,因为此时CollapsingToolbarLayout已经显示到状态栏了, 不需要在 Layout 过程中向下偏移, 但是此时collapsingToolbarLayout.setStatusBarScrimColor()也就无效了. - 通过
AppBarLayout的OnOffsetChangedListener()监听AppBarLayout的滑动, 利用setStatusBarColor()设置状态栏颜色即可.
- 变为全屏模式,设置 View 的
关于博客和库
博客主要提供思路解析,因为博客的通知不及时,大家有问题有想法想交流时还请在 Github issues 页联系.

