导航抽屉在状态栏上半透明不起作用

Posted

技术标签:

【中文标题】导航抽屉在状态栏上半透明不起作用【英文标题】:Navigation Drawer semi-transparent over status bar not working 【发布时间】:2014-12-31 22:37:18 【问题描述】:

我正在开发 android 项目,并且正在实现 Navigation Drawer。我正在阅读新的Material Design Spec 和Material Design Checklist。 规范说滑出窗格应该浮动在包括状态栏在内的所有其他内容之上,并且在状态栏上是半透明的。

我的导航面板位于状态栏上方,但没有任何透明度。我按照谷歌开发者博客中的建议,遵循了这篇 SO 帖子中的代码,上面的链接How do I use DrawerLayout to display over the ActionBar/Toolbar and under the status bar?。

下面是我的 XML 布局

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_drawer_layout"
    android:layout_
    android:layout_
    android:fitsSystemWindows="true">
    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical">
        <android.support.v7.widget.Toolbar
            android:id="@+id/my_awesome_toolbar"
            android:layout_
            android:layout_
            android:minHeight="?attr/actionBarSize"
            android:background="@color/appPrimaryColour" />
    </LinearLayout>
    <LinearLayout android:id="@+id/linearLayout"
        android:layout_
        android:layout_
        android:layout_gravity="left|start"
        android:fitsSystemWindows="true"
        android:background="#ffffff">
        <ListView android:id="@+id/left_drawer"
            android:layout_
            android:layout_
            android:choiceMode="singleChoice"></ListView>
    </LinearLayout>
</android.support.v4.widget.DrawerLayout>

以下是我的应用主题

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/appPrimaryColour</item>
        <item name="colorPrimaryDark">@color/appPrimaryColourDark</item>
        <item name="colorAccent">@color/appPrimaryColour</item>
        <item name="windowActionBar">false</item>
        <item name="windowActionModeOverlay">true</item>

    </style>

下面是我的应用程序 v21 主题

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/appPrimaryColour</item>
    <item name="colorPrimaryDark">@color/appPrimaryColourDark</item>
    <item name="colorAccent">@color/appPrimaryColour</item>
    <item name="windowActionBar">false</item>
    <item name="windowActionModeOverlay">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

下面是我的 onCreate 方法

protected void onCreate(Bundle savedInstanceState)

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
    setSupportActionBar(toolbar);

    mDrawerLayout = (DrawerLayout)findViewById(R.id.my_drawer_layout);
    mDrawerList = (ListView)findViewById(R.id.left_drawer);

    mDrawerLayout.setStatusBarBackgroundColor(
        getResources().getColor(R.color.appPrimaryColourDark));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    
        LinearLayout linearLayout = 
            (LinearLayout)findViewById(R.id.linearLayout);
        linearLayout.setElevation(30);
    

下面是我的导航抽屉的屏幕截图,显示顶部不是半透明的

【问题讨论】:

发布你的结果截图。 @Boardy 你设法让它工作了吗? 无法在状态栏中设置透明效果。请摆脱它。 【参考方案1】:

你的状态栏背景是白色的,你抽屉的背景LinearLayout。为什么?您是fitsSystemWindows="true" 的设置DrawerLayout 和其中的LinearLayout。这会导致您的LinearLayout 展开状态栏后面(它是透明的)。因此,将状态栏抽屉部分的背景设为白色。

如果您不希望抽屉延伸到状态栏后面(希望整个状态栏具有半透明背景),您可以做两件事:

1) 您可以简单地从 LinearLayout 中删除任何背景值,并为其中的内容背景着色。或者

2) 您可以从您的LinearLayout 中删除fitsSystemWindows="true"。我认为这是一种更合乎逻辑和更清晰的方法。您还将避免在状态栏下方投射阴影,因为您的导航抽屉不会延伸。

如果您希望抽屉延伸到状态栏后面并具有半透明的状态栏大小的覆盖,您可以使用ScrimInsetFrameLayout 作为抽屉内容的容器 (ListView) 并使用 @ 设置状态栏背景987654335@。当然,您可以将#4000 更改为您想要的任何内容。不要忘记在这里留下fitsSystemWindows="true"

或者,如果您不想覆盖内容而只显示纯色,您可以将LinearLayout 的背景设置为您想要的任何内容。不过不要忘记单独设置内容的背景!

编辑:您不再需要处理任何这些。请参阅Design Support Library 以更轻松地实现导航抽屉/视图。

【讨论】:

感谢您的帮助。很抱歉迟迟没有回来,忙得不可开交。我已经设置了另一个赏金来奖励你最初设置的答案,但它在我有时间实施你的建议之前就完成了。不幸的是,我必须等待 23 小时,所以我会尽快添加它 小心!使用选项 1 或 2,请务必注意您的透支(您可以在手机设置的开发选项中检查它们)。当我只设置内容的背景时,抽屉后面的所有东西也被绘制了。否则很好的答案,绝对节省了我一些时间:) 您能否解释一下我们需要使用设计支持库使用什么解决方案?我遇到了一些问题,即使 Chris Banes 发布了答案。【参考方案2】:

您需要做的就是为状态栏使用一些半透明的颜色。将这些行添加到您的 v21 主题中:

<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/desired_color</item>

不要忘记color(资源)的格式必须始终为#AARRGGBB。这意味着颜色也将包含 alpha 值。

【讨论】:

你是男人,但是......你能告诉我为什么这只适用于有抽屉的活动吗?其他的,那些没有的,他们将状态栏显示为灰色。【参考方案3】:

为什么不使用类似的东西?

<android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/drawer_layout"
        android:layout_
        android:layout_
        >

    <FrameLayout
            android:id="@+id/content_frame"
            android:layout_
            android:layout_/>

    <ListView
            android:id="@+id/left_drawer"
            android:layout_
            android:layout_
            android:layout_gravity="start"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:background="#80111111"/>
</android.support.v4.widget.DrawerLayout>

上面的代码使用android:background 中的 alpha 资源能够在操作栏中显示透明度。

正如其他答案所示,还有其他方法可以通过代码执行此操作。我上面的回答,在 xml 布局文件中是否有必要,在我看来很容易编辑。

【讨论】:

为什么不呢,但目前尚不清楚您正在更改什么以及这如何回答问题。 将分隔线设置为透明并在背景颜色中使用 alpha 代码【参考方案4】:

这里提到的所有答案都太老太冗长了。 与最新 Navigationview 一起使用的最佳且简短的解决方案是

@Override
public void onDrawerSlide(View drawerView, float slideOffset) 
    super.onDrawerSlide(drawerView, slideOffset);

    try 
        //int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP)
            // Do something for lollipop and above versions

        Window window = getWindow();

        // clear FLAG_TRANSLUCENT_STATUS flag:
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

        // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

        // finally change the color to any color with transparency

         window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDarktrans));

     catch (Exception e) 

        Crashlytics.logException(e);

    

这将在您打开抽屉时将您的状态栏颜色更改为透明

现在,当您关闭抽屉时,您需要再次将状态栏颜色更改为深色。所以您可以这样做。

@Override
public void onDrawerClosed(View drawerView) 
   super.onDrawerClosed(drawerView);
    try 
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) 
            // Do something for lollipop and above versions

            Window window = getWindow();

            // clear FLAG_TRANSLUCENT_STATUS flag:
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

            // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

            // finally change the color again to dark
            window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
        
     catch (Exception e) 
        Crashlytics.logException(e);
    

然后在主布局中添加一行,即

            android:fitsSystemWindows="true"

你的抽屉布局看起来像

            <android.support.v4.widget.DrawerLayout     
            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:id="@+id/drawer_layout"
            android:fitsSystemWindows="true"
            android:layout_
            android:layout_>

你的导航视图看起来像

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_
        android:layout_
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/drawer"
        />

我已经对其进行了测试并且它完全可以工作。希望它可以帮助某人。这可能不是最好的方法,但它运行顺利并且易于实施。 如果有帮助,请标记它。快乐的编码:)

【讨论】:

谢谢!这为我节省了很多时间。它与最新的 NavigationView 完美配合。对于无法使其工作的任何人,请确保您为“colorPrimaryDarktrans”选择了具有一定透明度的颜色,否则您根本看不到任何变化。 完美运行,唯一的缺点是状态栏在调用 onDrawerClosed 时会“闪烁”,但如果您将透明颜色 colorPrimaryDarktrans 设置为 #05000000,则几乎不明显 谢谢你对我有用@Harry Sharma【参考方案5】:

您需要将导航抽屉布局包装在 ScrimLayout 中。

ScrimLayout 基本上在您的导航抽屉布局上绘制了一个半透明的矩形。 要检索插图的大小,只需在 ScrimLayout 中覆盖 fitSystemWindows

@Override
protected boolean fitSystemWindows(Rect insets) 
    mInsets = new Rect(insets);
    return true;

稍后覆盖onDraw 以绘制一个半透明的矩形。

可以在 Google IO 应用源中找到 example 实现。 Here你可以看到它在布局xml中是如何使用的。

【讨论】:

【参考方案6】:

如果您希望导航面板位于状态栏上方并且在状态栏上方是半透明的。 Google I/O Android App Source 提供了一个很好的解决方案(APK in Play Store 没有更新到最新版本)

首先你需要一个ScrimInsetFrameLayout

/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* A layout that draws something in the insets passed to @link #fitSystemWindows(Rect), i.e. the area above UI chrome
* (status and navigation bars, overlay action bars).
*/
public class ScrimInsetsFrameLayout extends FrameLayout 
    private Drawable mInsetForeground;

    private Rect mInsets;
    private Rect mTempRect = new Rect();
    private OnInsetsCallback mOnInsetsCallback;

    public ScrimInsetsFrameLayout(Context context) 
        super(context);
        init(context, null, 0);
    

    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) 
        super(context, attrs);
        init(context, attrs, 0);
    

    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    

    private void init(Context context, AttributeSet attrs, int defStyle) 
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ScrimInsetsView, defStyle, 0);
        if (a == null) 
            return;
        
        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsView_insetForeground);
        a.recycle();

        setWillNotDraw(true);
    

    @Override
    protected boolean fitSystemWindows(Rect insets) 
        mInsets = new Rect(insets);
        setWillNotDraw(mInsetForeground == null);
        ViewCompat.postInvalidateOnAnimation(this);
        if (mOnInsetsCallback != null) 
            mOnInsetsCallback.onInsetsChanged(insets);
        
        return true; // consume insets
    

    @Override
    public void draw(Canvas canvas) 
        super.draw(canvas);

        int width = getWidth();
        int height = getHeight();
        if (mInsets != null && mInsetForeground != null) 
            int sc = canvas.save();
            canvas.translate(getScrollX(), getScrollY());

            // Top
            mTempRect.set(0, 0, width, mInsets.top);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Bottom
            mTempRect.set(0, height - mInsets.bottom, width, height);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Left
            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Right
            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            canvas.restoreToCount(sc);
        
    

    @Override
    protected void onAttachedToWindow() 
        super.onAttachedToWindow();
        if (mInsetForeground != null) 
            mInsetForeground.setCallback(this);
        
    

    @Override
    protected void onDetachedFromWindow() 
        super.onDetachedFromWindow();
        if (mInsetForeground != null) 
            mInsetForeground.setCallback(null);
        
    

    /**
     * Allows the calling container to specify a callback for custom processing when insets change (i.e. when
     * @link #fitSystemWindows(Rect) is called. This is useful for setting padding on UI elements based on
     * UI chrome insets (e.g. a Google Map or a ListView). When using with ListView or GridView, remember to set
     * clipToPadding to false.
     */
    public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) 
        mOnInsetsCallback = onInsetsCallback;
    

    public static interface OnInsetsCallback 
        public void onInsetsChanged(Rect insets);
    

然后,在您的 XML 布局中更改这部分

<LinearLayout android:id="@+id/linearLayout"
    android:layout_
    android:layout_
    android:layout_gravity="left|start"
    android:fitsSystemWindows="true"
    android:background="#ffffff">
    <ListView android:id="@+id/left_drawer"
        android:layout_
        android:layout_
        android:choiceMode="singleChoice"></ListView>
</LinearLayout>

像这样将 LinearLayout 更改为 ScrimInsetFrameLayout

<com.boardy.util.ScrimInsetFrameLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/linearLayout"
    android:layout_
    android:layout_
    android:layout_gravity="left|start"
    android:fitsSystemWindows="true"
    app:insetForeground="#4000">
    <ListView android:id="@+id/left_drawer"
        android:layout_
        android:layout_
        android:choiceMode="singleChoice"></ListView>
</com.boardy.util.ScrimInsetFrameLayout>

【讨论】:

嗨,谢谢你的回答。我找不到那个,我们从哪里得到 OnInsetsCallback ......提前谢谢。 你能把ScrimInsetsView_insetForeground属性放上去吗? 使用支持库中的android.support.design.internal.ScrimInsetsFrameLayout【参考方案7】:

我有一个类似的功能要在我的项目中实现。为了让我的抽屉透明,我只使用transparency for hex colours。使用 6 个十六进制数字,红色、绿色和蓝色的每个值分别有 2 个十六进制数字。但是如果你添加了两个额外的数字(8 个十六进制数字),那么你就有了 ARGB(alpha 值的两个数字)(Look here)。

这里是十六进制不透明度值:对于另外 2 个数字

100% — FF
95% — F2
90% — E6
85% — D9
80% — CC
75% — BF
70% — B3
65% — A6
60% — 99
55% — 8C
50% — 80
45% — 73
40% — 66
35% — 59
30% — 4D
25% — 40
20% — 33
15% — 26
10% — 1A
5% — 0D
0% — 00

例如:如果您有十六进制颜色 (#111111),只需输入一个不透明度值即可获得透明度。像这样:(#11111100)

我的抽屉没有盖住操作栏(就像你的一样),但在这些情况下也可以应用透明度。这是我的代码:

     <ListView
          android:id="@+id/left_drawer"
          android:layout_
          android:layout_
          android:layout_gravity="start"
          android:background="#11111100"
          android:choiceMode="singleChoice"
          android:divider="@android:color/transparent"
          android:dividerHeight="0dp" />
</android.support.v4.widget.DrawerLayout>

这里还有一个article,可以帮助你理解十六进制颜色的字母代码。

【讨论】:

【参考方案8】:

现有的解决方案相当过时。这是最新的解决方案,应该可以在 AndroidX 或 Material 库上完美运行。

android:fitsSystemWindows="true" 添加到布局的根视图中,这恰好是您的情况下的 DrawerLayout。

这告诉 View 使用整个屏幕进行测量和布局,与状态栏重叠。

将以下内容添加到您的 XML 样式中

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

windowTranslucentStatus 将使状态栏透明windowDrawsSystemBarBackgrounds 告诉状态栏在其下方绘制任何内容而不是黑色空白。

在主 Activity 中调用 DrawerLayout.setStatusBarBackground()DrawerLayout.setStatusBarBackgroundColor()

这告诉 DrawerLayout 为状态栏区域着色。

【讨论】:

【参考方案9】:

这里的所有答案都很复杂,让我给你简单的代码。 在你的 AppTheme 样式下,添加这行代码,打开抽屉你会得到一个灰色的半透明状态栏。

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

这将使它工作。

【讨论】:

以上是关于导航抽屉在状态栏上半透明不起作用的主要内容,如果未能解决你的问题,请参考以下文章

缩小操作栏后,导航抽屉切换不起作用

导航抽屉操作栏按钮不起作用

导航抽屉在 Android Studio 中不起作用

透明导航栏不起作用(呈半透明蓝色)

牛轧糖状态栏上的导航抽屉?

全屏透明活动(无标题和状态栏)不起作用....为啥?