397

I've seen in the new material design Side Nav spec that you can display the drawer over the action bar and behind the status bar. How can I implement this?

Chris Banes
  • 31,383
  • 16
  • 57
  • 50
  • 5
    Even today there is not a clear answer that works with backward compatibility. These kind of issues really exasperate any programmer. Already promoting the "N "API and many many basics aspects to improve in the Android system. – Val Martinez Jun 05 '16 at 14:40

10 Answers10

531

New functionality in the framework and support libs allow exactly this. There are three 'pieces of the puzzle':

  1. Using Toolbar so that you can embed your action bar into your view hierarchy.
  2. Making DrawerLayout fitsSystemWindows so that it is layed out behind the system bars.
  3. Disabling Theme.Material's normal status bar coloring so that DrawerLayout can draw there instead.

I'll assume that you will use the new appcompat.

First, your layout should look like this:

<!-- The important thing to note here is the added fitSystemWindows -->
<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">

    <!-- Your normal content view -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!-- We use a Toolbar so that our drawer can be displayed
             in front of the action bar -->
        <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="?attr/colorPrimary" />

        <!-- The rest of your content view -->

    </LinearLayout>

    <!-- Your drawer view. This can be any view, LinearLayout
         is just an example. As we have set fitSystemWindows=true
         this will be displayed under the status bar. -->
    <LinearLayout
        android:layout_width="304dp"
        android:layout_height="match_parent"
        android:layout_gravity="left|start"
        android:fitsSystemWindows="true">

        <!-- Your drawer content -->

    </LinearLayout>

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

Then in your Activity/Fragment:

public void onCreate(Bundled savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Your normal setup. Blah blah ...

    // As we're using a Toolbar, we should retrieve it and set it
    // to be our ActionBar
    Toolbar toolbar = (...) findViewById(R.id.my_awesome_toolbar);
    setSupportActionBar(toolbar);

    // Now retrieve the DrawerLayout so that we can set the status bar color.
    // This only takes effect on Lollipop, or when using translucentStatusBar
    // on KitKat.
    DrawerLayout drawerLayout = (...) findViewById(R.id.my_drawer_layout);
    drawerLayout.setStatusBarBackgroundColor(yourChosenColor);
}

Then you need to make sure that the DrawerLayout is visible behind the status bar. You do that by changing your values-v21 theme:

values-v21/themes.xml

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

Note: If a <fragment android:name="fragments.NavigationDrawerFragment"> is used instead of

<LinearLayout
    android:layout_width="304dp"
    android:layout_height="match_parent"
    android:layout_gravity="left|start"
    android:fitsSystemWindows="true">

    <!-- Your drawer content -->

</LinearLayout>

the actual layout, the desired effect will be achieved if you call fitsSystemWindows(boolean) on a view that you return from onCreateView method.

@Override
public View onCreateView(LayoutInflater inflater, 
                         ViewGroup container,
                         Bundle savedInstanceState) {
    View mDrawerListView = inflater.inflate(
        R.layout.fragment_navigation_drawer, container, false);
    mDrawerListView.setFitsSystemWindows(true);
    return mDrawerListView;
}
Mukesh Rana
  • 3,873
  • 3
  • 24
  • 38
Chris Banes
  • 31,383
  • 16
  • 57
  • 50
  • Do you need `android:fitsSystemWindows="true"` in both the DL and the LL? – a.bertucci Oct 18 '14 at 14:47
  • 3
    Yes, the `DrawerLayout` needs it to to be layed out behind the system bars. You then need to set it on the drawer view so that `DrawerLayout` lays it out within the window insets. – Chris Banes Oct 18 '14 at 14:53
  • Is there a "windowDrawsSystemBarBackgrounds" that can be set programmatically? – cottonBallPaws Oct 21 '14 at 20:23
  • Found it... window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) – cottonBallPaws Oct 21 '14 at 20:47
  • People not able to achieve by above method do look these threads http://stackoverflow.com/questions/26434504/how-to-implement-drawerarrowtoggle-from-android-appcompat-v7-21-library/26506745#answer-26447144 http://stackoverflow.com/questions/26434504/how-to-implement-drawerarrowtoggle-from-android-appcompat-v7-21-library/26506745#26506745 – Nitin Misra Oct 22 '14 at 11:48
  • 2
    Most activity needs ActionBar Theme instead of NoActionBar. And I need to setup global Buttons/TextView/EditText styles both on ActionBar/NoActionBar theme. But a style cant have two parents. What's your suggestion? – Tsung Goh Oct 22 '14 at 12:01
  • 1
    @ChrisBanes you mentioned that the status bar background set on the drawer is drawn on v19+ but I can see that `DrawerLayoutCompatImpl` no-ops on every other version than v21+. This means that on v19 the insets rect and other internal flags for enabling the background drawing are never set, the background never drawn, and every child of the drawer drawn behind the status bar. I could draw something behind the status bar from a different view, but this defeats the purpose of `setStatusBarBackground` as I would need to do the same thing. Am I missing something? – jankovd Oct 29 '14 at 15:09
  • I'm getting a status-bar-sized and background-colored bar at the top of my navigation drawer layout when using the code from this answer. It's odd, the drawer pane literally has a bar at the top of its layout, that if it were removed, would be 100% perfect. No where am I using any margin / padding that would cause this bar to appear. – Charles Madere Oct 30 '14 at 08:02
  • 11
    Can't get this to work at all. With the code above in a new demo application the result looks similar to what @ScootrNova describes. See this Screenshot: http://imgur.com/QLk3syt – Michael Schmidt Oct 30 '14 at 10:32
  • 1
    People with similar problems should definitely check out this answer: http://stackoverflow.com/a/26621718/1012557 It helped me a lot. – Michael Schmidt Oct 30 '14 at 11:16
  • 6
    Looks like you actually need to use a negative `layout_marginTop` in your drawer content's layout's root. Otherwise, your drawer content's layout's background will be drawn on top of the status bar, and everything else will be pushed down. This however seems like a kinda gross solution, as far as I know there is no ?attr/statusBarSize or anything like that supplied by Android. – Charles Madere Oct 31 '14 at 07:33
  • Question: How would you change the layout to make the drawer overlaps only the content, and not the action bar? It seems my trial to do so cause 2 toolbars to appear in case I use ActionMode. – android developer Oct 31 '14 at 11:09
  • 1
    Instead of minHeight="?attr/actionBarSize" you might want to do layout_height="?attr/actionBarSize" because otherwise it might mess up the whole thing in landscape (where theactionBarSize is different than in portrait) – Patrick Boos Nov 04 '14 at 09:00
  • 26
    To get the effect where the drawer is visible through the status bar, I ended up setting android:fitsSystemWindows to false on the DrawerLayout, android:fitsSystemWindows to true on my main content layout (the layout containing the Toolbar), and adding true to my theme - on Lollipop that is – Jakob Nov 06 '14 at 00:20
  • Question: For devices without `Lollipop` I would like to display the `DrawerLayout` below the `ActionBar/Toolbar` ([link](http://stackoverflow.com/q/26464326/1480019)), but for devices with `Lollipop` I would like to display the `DrawerLayout` over the `ActionBar/Toolbar` and under the status bar. Is this possible in one layout view (XML file)? Or should I use 2 layout views and check if `Build.VERSION.SDK_INT` is greater than or equal to `Build.VERSION_CODES.LOLLIPOP` and then `setContentView` to the appropriate layout view? – user1480019 Nov 10 '14 at 19:35
  • 8
    This answer is incomplete. You also need to wrap your navigation drawer inside a ScrimLayout. See http://stackoverflow.com/a/26926998/174149 – Markus Hi Nov 14 '14 at 09:45
  • 2
    @Jakob's answer works, but the `colorPrimary` attribute isn't applied anymore. I want it to look like [this](http://material-design.storage.googleapis.com/publish/v_2/material_ext_publish/0Bx4BSt6jniD7c2dhcFRZdzBPWnM/patterns_navdrawer_elevation1.png), but it looks like this [this](https://drive.google.com/file/d/0Bz-UGI12cm16eTZiYkRtaUZ4dnc/view?usp=sharing) (It's supposed to be dark red). Any suggestions? – bvx89 Dec 02 '14 at 02:10
  • @dominik4142 No, sorry. Do you mind starting a new question? Maybe someone else will see it and provide an answer. – bvx89 Dec 23 '14 at 23:45
  • 1
    Cannot get this working. Best what I could achieve was making toolbars grey and status bar to change color when opening drawer. Was there some changes in API? – joshas Dec 28 '14 at 16:09
  • 3
    @bvx89 The only solution i found is setting background color of the DrawerLayout to desired color. I hope this helps you. – ycagri Jan 02 '15 at 15:33
  • 1
    @ChrisBanes fitSystemWindows(Rect insets). This method was deprecated in API level 20. – IgorGanapolsky Jan 04 '15 at 21:08
  • 1
    @joshas I found out that using it with fragment as drawer does not work, when simple view it works correctly. – dominik4142 Feb 18 '15 at 14:55
  • Only thing needed to make this answer fully work is adding `true` to the theme in values-v21/styles.xml. I have updated the answer to reflect this. – Paul Burke Mar 07 '15 at 23:30
  • The solution for the fragment implementation doesn't work at all. The fragment content will appear as in the first screenshot here: http://stackoverflow.com/a/26932228/1720329 Any solution in order to push the content of the fragment below the status bar? I've already tried the ScrimInsetsFrameLayout and didn't make *any* difference. – Bogdan Zurac Apr 22 '15 at 13:36
  • 2
    @PaulBurke for me `windowTranslucentStatus` was causing toolbar to be overdrawn by statusbar. – Denis Kniazhev Jun 22 '15 at 11:22
  • 1
    can anybody post s working example project of this answer thanks – hariszaman Jun 25 '15 at 21:05
  • I removed `android:windowTranslucentStatus`, same as PaulBurke. I noticed that Chris Bane's sample app doesn't use this line. So I think it is safe to assume it shouldn't be here. – Brian Attwell Oct 28 '15 at 20:09
  • This way is outdated. Check the [Suyash's updated answer](http://stackoverflow.com/a/27153313/842697) – Brais Gabin Nov 04 '15 at 11:48
  • The answer is becoming quite old, but it is still in use by many people and it this is the SO question even Google Play Apps Editorial Board refers to on how to implement the navigation drawer (they don't mention which answer to go for). I followed this answer, but to make it work **I had to use android.support.design.widget.NavigationView** in the drawer and not "any view" with fitSystemWindows=true (tested on v22 and v23). – Roy Solberg Feb 09 '16 at 09:38
  • it works perfectly for me I just added true and android:fitsSystemWindows="true" – Manjeet Singh May 01 '17 at 00:02
  • `android:fitsSystemWindows` does nothing, navigation drawer is still below default action bar (or toolbar) – user25 Mar 24 '19 at 18:12
  • and you XML file doesn't include any toolbar... with default toolbar in style menu will be always below toolbar – user25 Mar 24 '19 at 18:13
140

EDIT: The new Design Support Library supports this and the previous method is no longer required.

This can now be achieved using the new Android Design Support Library.

You can see the Cheesesquare sample app by Chris Banes which demos all the new features.


Previous method:

Since there is no complete solution posted, here is the way I achieved the desired result.

First include a ScrimInsetsFrameLayout in your project.

/*
* 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 create a styleable so that the insetForeground can be set.

values/attrs.xml

<declare-styleable name="ScrimInsetsView">
    <attr name="insetForeground" format="reference|color" />
</declare-styleable>

Update your activity's xml file and make sure android:fitsSystemWindows is set to true on both the DrawerLayout as well as the ScrimInsetsFrameLayout.

layout/activity_main.xml

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

    <!-- The main content view -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!-- Your main content -->

    </LinearLayout>

    <!-- The navigation drawer -->
    <com.example.app.util.ScrimInsetsFrameLayout 
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrimInsetsFrameLayout"
        android:layout_width="320dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/white"
        android:elevation="10dp"
        android:fitsSystemWindows="true"
        app:insetForeground="#4000">

        <!-- Your drawer content -->

    </com.example.app.util.ScrimInsetsFrameLayout>

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

Inside the onCreate method of your activity set the status bar background color on the drawer layout.

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // ...

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
    mDrawerLayout.setStatusBarBackgroundColor(
        getResources().getColor(R.color.primary_dark));
}

Finally update your app's theme so that the DrawerLayout is behind the status bar.

values-v21/styles.xml

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

Result:

Suyash
  • 2,437
  • 2
  • 15
  • 26
  • 10
    This worked the best for me. I spent 2 days to get this thing work and this is the one. This should be marked as Answer. Thank you so much. – hvkale Dec 07 '14 at 21:36
  • 2
    I am not using appcompat and this didn't work. :( Is there any guide one getting this working without appcompat? – Jared Rummler Jan 01 '15 at 22:29
  • 3
    This is the most perfect answer. I have implemented and tested it in various Android devices from 4.X to 5.X and can confirm that it works perfectly. Thanks a lot. – Aritra Roy Jan 08 '15 at 20:17
  • 2
    As I said, it works perfectly, but I have a little problem. The activity with the Navigation Drawer has the transparent status bar, but all other activities have lost the "primaryDark" color from the status bar. How to get that back? – Aritra Roy Jan 11 '15 at 07:09
  • 5
    It took me three days to accept that there is currently no other way than using this additional layout wrapped around the drawer fragment. Otherwise the status bar will either be gray (first child's background color) or the drawer will be displayed below the status bar. – Denis Loh Jan 11 '15 at 14:06
  • @Aritra how so? the styles.xml only supports v21 – Atieh Apr 02 '15 at 04:38
  • @Suyash how do you have an actionbar when you specified none? Everything is working but I have no actionbar like in the photo.. – Atieh Apr 02 '15 at 04:41
  • @Atieh Use AppCompat21 & the Toolbar widget provided in the support library v7 inside the main content view LinearLayout. http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html – Suyash Apr 06 '15 at 16:41
  • I've tried exactly this solution for using a Fragment as a drawer instead of a LinearLayout and it still didn't work. the Drawer appears as the first screenshot from this answer http://stackoverflow.com/a/26932228/1720329 – Bogdan Zurac Apr 22 '15 at 13:37
  • Using this solution, my actionbar(toolbar) is drawn behind the statusbar aswell. – Michiel May 11 '15 at 13:52
  • For anyone with the issue described by @AritraRoy "all other activities have lost the "primaryDark" color", you can create a new style, called "MainActivityTheme" which inherits from your AppTheme, add the android:windowDrawsSystemBarBackgrounds and android:statusBarColor parameters to that theme only, and apply it to your Activity. –  Jul 18 '15 at 01:14
  • can you enter the `ScrimInsetsView_insetForeground` style – reegan29 Oct 15 '15 at 07:07
  • Could you please take a look at my question? I can't figure out my problem http://stackoverflow.com/questions/34456192/cannot-put-drawerlayout-under-statusbar – Pier Dec 27 '15 at 18:05
  • 1
    With the latest material design support library, you can also use `android.support.design.internal.ScrimInsetsFrameLayout` instead of including a copy of the class in your own project. – carloshwa Apr 03 '16 at 14:41
  • @carloshwa Or you can use the new NavigationView if you're using the latest design library. – Suyash Apr 03 '16 at 16:15
  • why navigation drawer is placed over status bar? – user25 Mar 24 '19 at 18:14
96

With the release of the latest Android Support Library (rev 22.2.0) we've got a Design Support Library and as part of this a new view called NavigationView. So instead of doing everything on our own with the ScrimInsetsFrameLayout and all the other stuff we simply use this view and everything is done for us.

Example

Step 1

Add the Design Support Library to your build.gradle file

dependencies {
    // Other dependencies like appcompat
    compile 'com.android.support:design:22.2.0'
}

Step 2

Add the NavigationView to your DrawerLayout:

<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="true"> <!-- this is important -->

     <!-- Your contents -->

     <android.support.design.widget.NavigationView
         android:id="@+id/navigation"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_gravity="start"
         app:menu="@menu/navigation_items" /> <!-- The items to display -->
 </android.support.v4.widget.DrawerLayout>

Step 3

Create a new menu-resource in /res/menu and add the items and icons you wanna display:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/ic_action_home"
            android:title="Home" />
        <item
            android:id="@+id/nav_example_item_1"
            android:icon="@drawable/ic_action_dashboard"
            android:title="Example Item #1" />
    </group>

    <item android:title="Sub items">
        <menu>
            <item
                android:id="@+id/nav_example_sub_item_1"
                android:title="Example Sub Item #1" />
        </menu>
    </item>

</menu>

Step 4

Init the NavigationView and handle click events:

public class MainActivity extends AppCompatActivity {

    NavigationView mNavigationView;
    DrawerLayout mDrawerLayout;

    // Other stuff

    private void init() {
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
        mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {
                mDrawerLayout.closeDrawers();
                menuItem.setChecked(true);
                switch (menuItem.getItemId()) {
                    case R.id.nav_home:
                        // TODO - Do something
                        break;
                    // TODO - Handle other items
                }
                return true;
            }
        });
    }
}

Step 5

Be sure to set android:windowDrawsSystemBarBackgrounds and android:statusBarColor in values-v21 otherwise your Drawer won`t be displayed "under" the StatusBar

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Other attributes like colorPrimary, colorAccent etc. -->
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

Optional Step

Add a Header to the NavigationView. For this simply create a new layout and add app:headerLayout="@layout/my_header_layout" to the NavigationView.

Result

picture showing navigation view

Notes

  • The highlighted color uses the color defined via the colorPrimary attribute
  • The List Items use the color defined via the textColorPrimary attribute
  • The Icons use the color defined via the textColorSecondary attribute

You can also check the example app by Chris Banes which highlights the NavigationView along with the other new views that are part of the Design Support Library (like the FloatingActionButton, TextInputLayout, Snackbar, TabLayout etc.)

reVerse
  • 33,862
  • 21
  • 85
  • 82
  • 1
    thanks @reVerse!!. > If you want to update every item and icon in the list from the style files, you can do it using: `@color/YOUR_COLOR` `@color/YOUR_COLOR` – Juan Saravia May 30 '15 at 19:56
  • How do you change the background color of the selected item? – Joop Jul 26 '15 at 18:18
  • 2
    This approach still produces the NavigationView that is behind the ActionBar. – southerton Aug 24 '15 at 17:24
  • 1
    @southerton of course, you have to use a `Toolbar` - there's no way to do this with an `ActionBar` – reVerse Aug 25 '15 at 22:59
  • @reVerse does this approach require the MainActivity to extend AppCompatActivity or can I extend regular Activity? I am using Halo Theme and it would require too many changes in the app to switch over to AppCompat Theme. Thank You. – Gary Kipnis Sep 17 '15 at 23:49
  • @GaryKipnis Yes. Since you have to use a `Toolbar` instead of a `ActionBar` you must declare `Theme.AppCompat.Light.NoActionBar` as the parent of your AppTheme. Because of that a descendant of `AppCompatActivity` is required. – reVerse Sep 18 '15 at 07:51
  • @reVerse - this is a bit off topic, but can you recommend an easy/efficient way to migrate from Halo Theme to AppCompat Theme. When I simply make the Theme change and change the parent activity from Activity to AppCompatActivity, all my activity windows (which have many listviews) have different fonts/styles and the ActionBar disappears completely (which at this point I am guessing requires explicitly instantiating a Toolbar) Thanks. – Gary Kipnis Sep 18 '15 at 19:46
  • @GaryKipnis Unfortunately there's no easy way. But you can use `Theme.AppCompat.Light.DarkActionBar` for example as the parent theme in the Activities where you want to continue to use `ActionBars`. You don't have to use Toolbars everywhere just because of AppCompatActivity. In terms of the different fonts/styles: It's hard to tell what's affecting the different views without seeing your styles.xml file. Nevertheless: I'd say it's no easy to "migrate" to Material Design from Holo but it's definitely worth the trouble. – reVerse Sep 18 '15 at 20:40
  • I want to trigger the Drawer using bars on the top of ActionBar or ToolBar ..Can you update how to do this in your example .. – reegan29 Oct 15 '15 at 12:33
  • 1
    @reegan29 Simply call `mDrawerLayout.openDrawer(GravityCompat.START);` wherever you want. If you're using an `ActionBarDrawerToggle` this should be done automatically as soon as you click on the hamburger icon. – reVerse Oct 15 '15 at 12:55
  • NavigationView works great. However, I am having trouble trying to get items to display using a custom layout. I am unable to adjust margins and style when using the menu item actionLayout attribute. Has anyone run into this? – masterwok Oct 03 '16 at 00:11
6

Make it work, in values-v21 styles or theme xml needs to use this attribute:

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

That make the magic!

Nicolas Jafelle
  • 2,352
  • 2
  • 20
  • 27
  • 3
    This makes my actionbar appear behind the statusbar as well. – Michiel Nov 05 '14 at 09:01
  • But that is the effect that we are trying to achieve, look at the Gmail 5.0 app? It shows the Side Bar behind the status bar. – Nicolas Jafelle Nov 05 '14 at 13:03
  • That's not what i meant to say. Eventhough the navigation drawer is behind the statusbar with this line of code, so is the toolbar/actionbar. – Michiel Nov 05 '14 at 15:55
  • 2
    The poster and answerer is a Google employee who designed AppCompat, so I'm pretty sure he knew what he was doing when he wrote the content. – afollestad Nov 17 '14 at 00:55
  • @Mixx, You can set android:fitsSystemWindows="true" for your normal content view (if taken the accepted answer layout for example). It can work, even for Android 4.4. But there is a slight difference here: In Android 5.0 the transparency level on the status bar is somewhat higher (higher alpha value) than that in GMail, making the status bar even darker. – Qianqian Dec 14 '14 at 12:00
  • Is this possible in Android 4.4 and below? – Sotti Mar 10 '15 at 16:47
6

The above all approaches are correct and may be working . I have created a working demo following the above guide and tested on 2.x to 5.x

You can clone from Github

The important thing to play around is in Main Activity

toolbar = (Toolbar) findViewById(R.id.toolbar);
res = this.getResources();

this.setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
    ScrimInsetsFrameLayout scrimInsetsFrameLayout = (ScrimInsetsFrameLayout)
            findViewById(R.id.linearLayout);
    scrimInsetsFrameLayout.setOnInsetsCallback(this);
} 

and the call back

@Override
public void onInsetsChanged(Rect insets) {
    Toolbar toolbar = this.toolbar;
    ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
        toolbar.getLayoutParams();
    lp.topMargin = insets.top;
    int top = insets.top;
    insets.top += toolbar.getHeight();
    toolbar.setLayoutParams(lp);
    insets.top = top; // revert
}

Absolutely the Theme for V21 does the magic

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- API 21 theme customizations can go here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/accent_material_light</item>
    <item name="windowActionModeOverlay">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowTranslucentStatus">true</item>
</style>

and the ScrimInsetsFrameLayout

Now this come more easy with new Design Support library

compile 'com.android.support:design:22.2.0'

clone from @Chris Banes https://github.com/chrisbanes/cheesesquare

Vipin Sahu
  • 1,431
  • 17
  • 28
4

I am Using Design Support Library. And just by using custom theme I achived transparent Status Bar when Opened Navigation Drawer.

enter image description here

enter image description here

<style name="NavigationStyle" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/primaryColor</item>
    <item name="colorPrimaryDark">@color/primaryColorDark</item>

    <!-- To Make Navigation Drawer Fill Status Bar and become Transparent Too -->
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>

</style>

Finally add theme in Manifest File

<activity
  ........
  ........
 android:theme="@style/NavigationStyle"> 
</activity>

Do not forget to use the property, android:fitsSystemWindows="true" in "DrawerLayout"

katwal-Dipak
  • 2,985
  • 1
  • 21
  • 23
4

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.

        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 :)

Harry Sharma
  • 2,090
  • 1
  • 12
  • 35
  • Alternatively, if you are using a custom view in ActionBar, you could set the custom view's visibility to INVISIBLE in onDrawerSlide() and to VISIBLE in onDrawerClosed(). – Milan Jul 06 '16 at 08:29
2

This is the most simple, and it worked for me:

In the values-21:

<resources>
    <style name="AppTheme" parent="AppTheme.Base">
        ...
        <item name="android:windowTranslucentStatus">true</item>
    </style>
    <dimen name="topMargin">25dp</dimen>
</resources>

In the values:

<resources>
    <dimen name="topMargin">0dp</dimen>
</resources>

And set to your toolbar

android:layout_marginTop="@dimen/topMargin"
Nicolás López
  • 142
  • 1
  • 5
  • I also did this, and it worked like a charm on Lollipop. But I used `24dp` for top margin. – Rafa Apr 19 '15 at 21:52
  • When you divide the screen on devices with Android 7.1 you can see this gap on the top if you app is the second part of the screen. – Cícero Moura Nov 03 '17 at 12:44
1

Instead of using the ScrimInsetsFrameLayout... Isn't it easier to just add a view with a fixed height of 24dp and a background of primaryColor?

I understand that this involves adding a dummy view in the hierarchy, but it seems cleaner to me.

I already tried it and it's working well.

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_base_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">

        <!-- THIS IS THE VIEW I'M TALKING ABOUT... -->
        <View
            android:layout_width="match_parent"
            android:layout_height="24dp"
            android:background="?attr/colorPrimary" />

        <android.support.v7.widget.Toolbar
            android:id="@+id/activity_base_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:elevation="2dp"
            android:theme="@style/ThemeOverlay.AppCompat.Dark" />

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

    </LinearLayout>

    <fragment
        android:id="@+id/activity_base_drawer_fragment"
        android:name="com.myapp.drawer.ui.DrawerFragment"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:elevation="4dp"
        tools:layout="@layout/fragment_drawer" />

</android.support.v4.widget.DrawerLayout>
mato
  • 1,291
  • 9
  • 17
  • When you divide the screen on devices with Android 7.1 you can see this gap on the top if you app is the second part of the screen. – Cícero Moura Nov 03 '17 at 12:44
0

Try with this:

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


<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--Main layout and ads-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/ll_main_hero"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

        </FrameLayout>

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

            <View
                android:layout_width="320dp"
                android:layout_height="50dp"
                android:layout_gravity="center"
                android:background="#ff00ff" />
        </FrameLayout>


    </LinearLayout>

    <!--Toolbar-->
    <android.support.v7.widget.Toolbar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/toolbar"
        android:elevation="4dp" />
</FrameLayout>


<!--left-->
<ListView
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:choiceMode="singleChoice"
    android:divider="@null"
    android:background="@mipmap/layer_image"
    android:id="@+id/left_drawer"></ListView>

<!--right-->
<FrameLayout
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:layout_gravity="right"
    android:background="@mipmap/layer_image">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ken2"
        android:scaleType="centerCrop" />
</FrameLayout>

style :

<style name="ts_theme_overlay" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/red_A700</item>
    <item name="colorPrimaryDark">@color/red1</item>
    <item name="android:windowBackground">@color/blue_A400</item>
</style>

Main Activity extends ActionBarActivity

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

Now you can onCreateOptionsMenu like as normal ActionBar with ToolBar.

This is my Layout

  • TOP: Left Drawer - Right Drawer
    • MID: ToolBar (ActionBar)
    • BOTTOM: ListFragment

Hope you understand !have fun !

Son Nguyen Thanh
  • 1,049
  • 13
  • 19