使用SystemBarTint搭配NavigationView在Android4.4以上实现透明状态栏

快来看看 Android 上的抽屉导航之美吧~

前言

在 Android 中,很多 App 的主界面都使用了抽屉式导航,比如网易云音乐、Fuubo、Bilibili等,就我个人而言,我是非常喜欢这种导航方式的,顺便吐槽一下新版知乎,导航模式改为了底部导航,还取消了滑动返回,用的心累。

这是官方给出的示例图片,我觉得非常的漂亮,尤其是抽屉导航在状态栏的下方,当滑动抽屉的时候可以很明显的看出状态栏与抽屉之间的层次关系,而这也是今天我们要实现的效果,以下示例都运行在4.4版本上。

实现

首先看两个比较关键的属性,一个是:windowTranslucentStatus 这个属性支持的API最低为19,通常定义在 values-v19/styles.xml 下,它的作用是可以将状态栏的颜色置为透明色,还有一个是 fitsSystemWindows,这个属性的作用是确保内容不会显示到系统窗口下面,这里的系统窗口指的是 StatusBar 和NavigationBar。

全透明状态栏

首先看看网易云音乐在4.4上的表现

可以看到抽屉是在状态栏下面的,并且状态栏是完全透明的,要实现这样的效果也是很简单的,使用 Toolbar 替换 ActionBar,在 values-v19/styles.xmlvalues-v21/styles.xml 中定义 windowTranslucentStatus 为 true。
代码如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="AppTheme.Base">
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>

activity_main 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<include layout="@layout/toolbar" />

<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false"
app:headerLayout="@layout/drawer_header"
app:menu="@menu/menu_drawer" />
</android.support.v4.widget.DrawerLayout>

要注意的是设置 fitsSystemWindows 为 false。
其实到这里就已经实现了这个效果,不过可以发现 toolbar 会向上移动,有一部分被状态栏遮住,其实遮住的这一部分就是状态栏的高度,因为设置为 true 会,解决办法是可以在 Activity 中为 toolbar 添加顶部的 padding,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity extends AppCompatActivity {

private Toolbar mToolbar;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setPadding(0,getStatusBarHeight(),0,0);
}

public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

最终效果:

着色状态栏

这种抽屉方式,在滑动的时候给人一种撕裂的感觉,看起来不是很舒服。

因为要给 statusbar 着色,所以要用到第三方库SystemBarTint,但要注意这个库已经被作者标记为 DEPRECATED,即作者不赞成现在使用该库,这个与今天主题无关,就不多管了。
Android4.4(Kitkat)提出了透明状态栏的概念,可以为 status bar 或是Navigation bar 设置透明度,而 SystemBarTin t的出现使我们更加方便为status bar 和 Navigation bar着色,可以设置color或是Drawable。
首先必须允许 status bar 透明,在 values-v19/styles.xml 中添加

1
<item name="android:windowTranslucentStatus">true</item>

activity_main 代码与上一个基本相同,只是不再需要 fitsSystemWindows 属性。
Activity 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends AppCompatActivity {

private Toolbar mToolbar;
private SystemBarTintManager mTintManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setPadding(0, getStatusBarHeight(), 0, 0);
setTintManager();
}

@TargetApi(19)
private void setTintManager() {
mTintManager = new SystemBarTintManager(this);
mTintManager.setStatusBarTintEnabled(true);
mTintManager.setStatusBarTintColor(Color.parseColor("#2196F3"));
}

...
}

最终效果:

这种其实就是实现官方给出的那种效果,也需要搭配 SystemBarTint,代码与相当于是上面两种的结合,只不过在设置 statusbar 颜色的时候设置的是 #20000000

1
2
3
4
5
6
@TargetApi(19)
private void setTintManager() {
mTintManager = new SystemBarTintManager(this);
mTintManager.setStatusBarTintEnabled(true);
mTintManager.setStatusBarTintColor(Color.parseColor("#20000000"));
}

最终效果:

参考

Android and the transparent status bar

该使用 fitsSystemWindows 了!