让导航抽屉在状态栏后面绘制。

44

我正在尝试创建一个导航抽屉,就像Material规范中的那个一样(就像新版Gmail应用程序中的那个一样)。请注意,导航抽屉的内容绘制在状态栏后面:

example from the Material spec

使用克里斯·班恩斯从这个问题中得到的答案,我成功地使我的应用程序中的导航抽屉在状态栏后面绘制;这很好地工作了。但是抽屉内容的绘制并没有成功地在状态栏后面。我希望我的抽屉中的蓝色图像能够显示在状态栏后面,但是该区域绘制的颜色为状态栏的颜色,如此截图所示。

my app

那么,我如何让我的导航抽屉在状态栏后面绘制? 我在下面发布了相关部分的项目。

包含导航抽屉的基本布局:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <!-- Framelayout to display Fragments -->
    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/warning_container" />

    <FrameLayout
        android:id="@+id/navigation_drawer_fragment_container"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:layout_gravity="start">

        <fragment
            android:id="@+id/navigation_drawer_fragment"
            android:name="com.thebluealliance.androidclient.fragments.NavigationDrawerFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:layout="@layout/fragment_navigation_drawer" />

    </FrameLayout>

</android.support.v4.widget.DrawerLayout>

我的活动主题

<style name="AppThemeNoActionBar" parent="AppTheme">
    <item name="windowActionBar">false</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

在我的活动的onCreate()中,我执行以下操作:
mDrawerLayout.setStatusBarBackground(R.color.primary_dark);

你看过 Chris Banes 的问答吗?https://dev59.com/wl8d5IYBdhLWcg3wxkpq - matiash
6
你读了我的问题吗?我明确提到我看了他的答案并按照他的指示操作了。 - Nathan Walters
抱歉,显然我没有像我想象的那样仔细阅读它。 :) - matiash
6个回答

53

适用于API版本21及以上

<style name="AppTheme" parent="android:Theme.Holo.NoActionBar.TranslucentDecor">
    ...
</style>

适用于API 19及以上版本

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowTranslucentStatus">true</item>
</style>

您的布局应具有android:fitsSystemWindows="false"(这是默认设置)。


现在,由于您想要切换半透明效果,您可以通过编程方式实现:

Window window = getWindow();

// Enable status bar translucency (requires API 19)
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

// Disable status bar translucency (requires API 19)
window.getAttributes().flags &= (~WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

// Set a color (requires API 21)
window.setStatusBarColor(Color.RED);

我将所有SDK版本检查留给你 :)


ss


@NathanWalters 你为什么认为 Gmail 使用 AppCompat? - Simas
1
他们在Android 5.0以外的版本上提供了Material Design,这一事实非常重要。如果他们已经在创建AppCompat时投入了大量时间,那么复制所有功能将是非常无意义的。 - Nathan Walters
@NathanWalters 我不确定你所说的“让平台在状态栏中绘制”的意思。没有人在其上或其上方进行绘制。它只是透明的,您可以看到位于其下面的视图。 - Simas
1
@NathanWalters,我的回答有什么问题吗?我已经回答了你的问题。 - Simas
我发现使用Android 5.0可以最好地完成此操作,详见我的新回答。不过,我会点赞你的回答,因为对于使用KitKat的人来说可能很有用,而且你显然花了很多心思 :) - Nathan Walters
显示剩余7条评论

17
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

        <include layout="@layout/toolbar" />
        <!-- Main layout -->
        <FrameLayout
            android:id="@+id/main_fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
    <!-- Nav drawer -->
    <fragment
        android:id="@+id/fragment_drawer"
        android:name="com.example.DrawerFragment"
        android:layout_width="@dimen/drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="left|start"
        android:fitsSystemWindows="true" />

</android.support.v4.widget.DrawerLayout>

values/themes.xml

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@color/primary</item>
    <item name="colorPrimary">@color/primary</item>
    <item name="colorPrimaryDark">@color/primaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:textColorPrimary">@color/textColorPrimary</item>
</style>

<style name="AppTheme" parent="AppTheme.Base">
</style>

values-v19/themes.xml

<style name="AppTheme" parent="AppTheme.Base">
    <!--This makes the status bar transparent in KK and Lollipop-->
    <!--You do not need values-v21 and if you create them make sure you extend from this one-->
    <item name="android:windowTranslucentStatus">true</item>
</style>

如果您想改变状态栏(非透明黑色)的颜色,您需要使用自定义视图采用其他方法,因为只有当这个DrawerLayout适配系统窗口 (android:fitsSystemWindows="true") 时, mDrawerLayout.setStatusBarBackgroundColor(int) 才会生效,如果您这样做了,它将不会被绘制在状态栏后面而是在其下方。


这个问题和官方答案的问题在于它没有着色状态栏。第一个子元素(LinearLayout)的背景色被用来代替提供的颜色。 - tomrozb
我在进行更多的测试后修改了答案。 - jpardogo
ScrimInsetFrameLayout 对于三星设备是必要的,因为它们默认不会使用透明黑色来绘制状态栏。因此,为了保持设备间的一致性,您需要使用 ScrimInsetFrameLayout。 - jpardogo

11

我发现在Android 5.0上实现这一点的最佳方法是使用ScrimInsetFrameLayout作为导航抽屉的根元素(即DrawerLayout中的第二个视图)。这将使内容扩展到填充状态栏后面的空间。要正确设置插入色,您可以在ScrimInsetFrameLayout上设置以下属性:

app:insetForeground="#4000"

另外,请确保在scrim布局上有android:fitsSystemWindows="true"

ScrimInsetsFrameLayout的源代码可以在此处找到:https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/ScrimInsetsFrameLayout.java


3
ScrimInsetFrameLayout不必要。我不知道为什么这个答案被接受了。 - jpardogo
1
@jpardogo那么最好的方法是什么?我认为谷歌设计其应用程序的方式应该是最正确的方式。 - Nathan Walters
1
不,Lollipop并没有发布,但Material Design也没有。这种行为是随着I/O的更新而来的。无论如何,我刚刚尝试了你的方法,得到的结果和之前一模一样。 - Nathan Walters
1
你的解决方案给了我最初的效果:导航抽屉在状态栏后面延伸,但是导航抽屉的内容没有。我直接使用了你的解决方案。 - Nathan Walters
@NathanWalters有解决这个问题的方法吗?https://dev59.com/ruo6XIcBkEYKwwoYGwQZ - Andrew Quebe
显示剩余6条评论

1
对于那些在导航栏和半透明状态栏组合使用时遇到困难,但不想更改<style name="yourAppTheme" parent="android:Theme.Holo.NoActionBar.TranslucentDecor"的人,只需在您的style.xml中添加以下几行代码即可:
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowTranslucentNavigation">true</item> 
    <item name="android:windowContentOverlay">@null</item>

0

只需将这两个值添加到您的主题样式中的values-v21文件中即可。

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
  ..
  <item name="android:statusBarColor">@android:color/transparent</item>
   <item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>

0
在你的“values-v21”目录中,添加以下行:
<style name="AppTheme" parent="BaseTheme">
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowSharedElementsUseOverlay">false</item>
</style>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接