前言:最近项目大量用到状态栏透明,网上也出现很多库可以直接拿来用,个人认为没有必要那么重引用到一个库(有木有同学和我有一样的想法),所以研究了一番,在此做个记录加强记忆也便后期查阅,如果无意中有幸能帮助到你那就再好不过了。
一、两个基本概念
Android 从 4.4 (SDK 19) 开始支持系统栏(状态栏+导航栏)半透明效果:
You can now make the system bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecor and Theme.Holo.Light.NoActionBar.TranslucentDecor. By enabling translucent system bars, your layout will fill the area behind the system bars, so you must also enable fitsSystemWindows for the portion of your layout that should not be covered by the system bars.
If you're creating a custom theme, set one of these themes as the parent theme or include the windowTranslucentNavigation and windowTranslucentStatus style properties in your theme.
翻译一下就是:
通过使用 Theme.Holo.NoActionBar.TranslucentDecor 或 Theme.Holo.Light.NoActionBar.TranslucentDecor 主题,可以实现系统栏半透明的效果。但是,这样一来系统栏会覆盖在布局上(实际上是内容延伸到了系统栏原本所占的区域下面),为了使布局不被系统栏覆盖,需要设置 fitsSystemWindows 属性为 true。
TranslucentDecor
主题设置了两个属性windowTranslucentStatus
和windowTranslucentNavigation
都为 true,前者指定状态栏半透明、后者指定导航栏半透明。
本文只探讨“状态栏”。
默认样式是这样:
可见 Toolbar 和系统状态栏之间有明显的分界,我们要实现的效果是 Toolbar 和状态栏背景统一,看起来像是一个整体(自行脑补图片)。
按照官方文档,我们自定义主题:
<style name="AppTheme.NoActionBar.TransparentStatusBar">
<item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
</style>
对应的 Activity 引用该主题:
<activity
android:name=".DetailActivity"
android:label="@string/title_activity_detail"
android:theme="@style/AppTheme.NoActionBar.TransparentStatusBar">
</activity>
我看来看看效果:
重要:图上图所示,在 TranslucentStatusBar 主题下,Android 4.4 状态栏背景为默认黑色到透明的渐变,5.0+ 状态栏背景默认为半透明的黑色。
虽然实现了半透明,但是布局被状态栏覆盖,接下来在布局文件中设置fitSystemWindows
(注意加到根节点ConstraintLayout
上):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.netease.mail.statusbar.DetailActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/AppTheme.AppBarOverlay"
app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/app_bar"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
</android.support.constraint.ConstraintLayout>
来看看效果:
虽然布局没有被状态栏覆盖,但是状态栏背景显然这不是我们想要的效果😭
为什么状态栏会这么奇怪?
文章开头的定义中我们说了,布局文件会延伸到状态栏所占区域下,fitsSystemWindows
的作用是给对应的 View 增加 padding(这里以 ConstraintLayout 为例),目的是为了让其内容不被状态栏遮挡。
在我们的布局文件中 ConstraintLayout 没有设置背景(默认白色),所以状态栏默认的半透明背景色和 ConstraintLayout 的白色背景叠加,就变成了上图中的效果。
【总结】两个基本概念:
1、windowTranslucentStatus
设置为true之后,状态栏默认是半透明的(4.4 是黑色到透明色渐变,5.0+ 是纯黑色半透明),和我们要求的透明相去甚远。更重要的是,布局会延伸到状态栏底下。
2、android:fitsSystemWindows
简单理解就是 View 为了适配系统状态栏和导航栏(不被遮挡)自动增加 padding,当然真正的实现原理比这复杂很多而且不同的 View 可以自定义实现方式。
二、透明效果如何实现
所以,为了实现文章开头提出来的“状态栏透明”效果,我们需要处理:
2.1 设置主题
设置 windowTranslucentStatus
为 true,让状态栏半透明。
2.2 布局自适应状态栏
在根节点设置 android:fitsSystemWindows
使其不被状态栏遮挡。
2.3 状态栏透明
Android 4.4 暂时没有办法去掉状态栏的渐变。
Android 5.0+ 开始支持修改状态栏颜色,设置透明色即可把半透明去掉。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//兼容5.0及以上支持全透明
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
activity.window.statusBarColor = Color.TRANSPARENT
}
通过 theme 设置无效。
看看效果:
2.4 给布局上色
我们看到即使状态栏透明了,但是其底色是一片白,因为跟节点 ConstraintLayout 没有设置背景,大多情况下我们不会给整个跟节点设置颜色,可以考虑把 android:fitsSystemWindows
设置到子 View 上,本例中是 AppBarLayout
(5.0+ 无效,只能显式给 AppBarLayout 加 padding,可以利用其背景色),实际项目中可灵活调整。
最终效果:
至此,完成状态栏透明效果,网上有很多库,实际上都是基于此原理,在此基础上再自定义 View 做为状态栏背景。
参考
https://developer.android.com/about/versions/android-4.4.html