导航抽屉半透明状态栏不工作

我正在进行 Android 项目,我正在实现导航抽屉。我正在阅读新的 材料设计规范材料设计清单
规范说滑出窗格应该浮动在包括状态栏在内的所有内容之上,并且在状态栏上是半透明的。

我的导航面板位于状态栏上方,但它没有任何透明度。我已经按照这篇 那么文章中的代码,在谷歌开发人员的博客地点,链接上面的 如何使用 DrawerLayout 在 ActionBar/Toolbar 和状态栏下显示?

下面是我的 XML 布局

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/my_awesome_toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="@color/appPrimaryColour" />
</LinearLayout>
<LinearLayout android:id="@+id/linearLayout"
android:layout_width="304dp"
android:layout_height="match_parent"
android:layout_gravity="left|start"
android:fitsSystemWindows="true"
android:background="#ffffff">
<ListView android:id="@+id/left_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
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);
}

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

Screenshot showing non transparent over the status bar

100577 次浏览

All you need to do is to use some semi-transparent color for status bar. Add these lines to your v21 theme:

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

Don't forget that the color (resource) must always be in the format of #AARRGGBB. This means that the color will also include the alpha value.

Why not use something similar to this?

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


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


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

The code above uses the alpha resource in the android:background to be able to show transparency within the actionbar.

There are other ways to do this through code, as other answers show. My answer above, does the necessary in the xml layout file, which in my opinion is easily edited.

You need to wrap your navigation drawer layout inside a ScrimLayout.

A ScrimLayout basically draws a semi-transparent rectangle over your navigation drawer layout. To retrieve the size of the inset simply override fitSystemWindows in your ScrimLayout.

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

Later override onDraw to draw a semi-transparent rectangle.

An example implementation can be found in the Google IO App source. Here you can see how it's used in the layout xml.

I had a similar feature to implement in my project. And to make my drawer transparent i just use the transparency for hex colours. Using 6 hex digits, you have 2 hex digits for each value of red, green and blue respectively. but if you put two additional digits (8 hex digits), so you have ARGB ( two digits for alpha value)(Look here).

Here are the Hex Opacity Values: for the 2 additional digits

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

For exemple: if you have de hex colour (#111111) just put one of opacity values to get transparency. like this: (#11111100)

My drawer does not cover the action bar (like yours) but the transparency can be applied in these cases also. Here's my code:

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

And here is another article that can help you understand the alpha codes in hex colors.

Your status bar background is white, the background of your drawer LinearLayout. Why? You are settings fitsSystemWindows="true" for your DrawerLayout and the LinearLayout inside it. This causes your LinearLayout to expand behind the status bar (which is transparent). Thus, making the background for the drawer part of the status bar white.

enter image description here
If you don't want your drawer to extend behind the status bar (want to have a semi-transparent background for the whole status bar), you can do two things:

1) You can simply remove any background value from your LinearLayout and color the background of your content inside it. Or

2) You can remove fitsSystemWindows="true" from your LinearLayout. I think this is a more logical and cleaner approach. You will also avoid having a shadow being cast under the status bar, where your navigation drawer doesn't extend.

enter image description here
If you want your drawer to extend behind the status bar and have a semi-transparent, status bar sized overlay, you can use a ScrimInsetFrameLayout as a container for your drawer content (ListView) and set the status bar background using app:insetForeground="#4000". Of course, you can change #4000 to anything you want. Don't forget to keep fitsSystemWindows="true" here!

Or if you don't want to overlay your content and only display a solid color, you can just set the background of your LinearLayout to anything you want. Don't forget to set the background of your content separately though!

EDIT: You no longer need to deal with any of this. Please see Design Support Library for a times easier Navigation Drawer/View implementation.

If you want navigation panel is over the status bar and be semi-transparent over the status bar. Google I/O Android App Source provides a good solution (APK in Play Store are not updated to lasted version)

First you need a 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);
}
}

Then, in your XML layout change this part

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

Change LinearLayout to your ScrimInsetFrameLayout like this

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

All answers mentioned here are too old and lengthy. The best and short solution that work with latest Navigationview is

@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);


}
}

this is going to change your status bar color to transparent when you open the drawer

Now when you close the drawer you need to change status bar color again to dark.So you can do it in this way.

@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);
}
}

and then in main layout add a single line i.e

            android:fitsSystemWindows="true"

and your drawer layout will look like

            <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_width="match_parent"
android:layout_height="match_parent">

and your navigation view will look like

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

I have tested it and its fully working.Hope it helps someone.This may not be the best approach but it works smoothly and is simple to implement. Mark it up if it helps.Happy coding :)

All answer here are very complicated, let me give you straight forward simple code. Under your AppTheme style , add this line of code and you will get a grey semi transparent status bar when you open the drawer.

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

this will make it work.

The existing solutions are fairly outdated. Here's the latest solution which should work perfectly on AndroidX or Material libraries.

  • Add android:fitsSystemWindows="true" to the root View of your layout, which happens to be DrawerLayout for your case.

    This tells the View to use the entire screen for measure & layout, overlapping with the status bar.

  • Add the following to your XML style

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

    windowTranslucentStatus will make the status bar transparent
    windowDrawsSystemBarBackgrounds tells the status bar to draw anything under it instead of being a black void.

  • Call DrawerLayout.setStatusBarBackground() or DrawerLayout.setStatusBarBackgroundColor() in your main Activity

    This tells DrawerLayout to color the status bar area.