Navigation Drawer With Transparent Status bar

It is second part of  Let's Start with Material Design Series. In this series i will explain how you can give Material over haul to your existing app or how you can create it with Material design. in first part i have created the main layout of the app so if you want to know about it head over to it. Part -1 is about using Recycler view and Toolbar in Pre - Lollipop device using appcompat v21 library. In this part i am going to explain about navigation drawer. how you can implement navigation drawer as per material design guidelines.This tutorial even includes hamburger animation used in Google apps.

According to android documentation If you have hierarchy of views(More than 3 top-level views) then only you have to implement nav drawer in your app. and all top level views must be displayed in nav drawer for quick jumps to the top-level of your app. As nav drawer can be used from all views of app, you can incorporate or display important lower-level views also. but make sure child view indicate its parent view so user won't get confused.

You can use list view or expandable list view for navigation drawer but make sure for each row you use only one text view. for this tutorial i am going to create nav drawer as you can see in google apps (not the same). but as per Material design guidelines, as guidelines suggest drawer should spans the full height of the screen and the drawer should below the status bar. to span full space we need to customize frame layout. That actually Making DrawerLayout behind the system bars using fitSystemWindows property. one more thing we are using toolbar in place of action bar (that look same as action bar) that's how we will overlapping nav drawer above the action bar. as i am using appcompat library this app will look like same on pre-lollipop android device also.

As per doc the width of the side nav is equal to the width of the screen minus the height of the action bar, or 56dp from the right edge of the screen.
Mobile: Width = screen width - app bar height (No wider than 320dp on phones and 400dp on tablets).

Step -1 : As we have already created project in Part -1 we are directly going to change the main layout of project. so put below code inside Main.xml file. as it consist ScrimInsetsFrameLayout which will give error but don't worry it is customized frame layout. as you go through full tutorial you will get class file for it also. The same is used in Google I/O app. why they have given such name even i can't understand.  you can give name what ever you want. Below xml files are layout for main(content view) and navigation drawer view.
Main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    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"
    tools:context=".MainActivity">

    <!-- Setting this one to true makes the status bar
        to have a non-transparent shitty background -->

    <!--
         As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions.
    -->

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

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

        <android.support.v7.widget.RecyclerView
            android:id="@+id/my_recycler_view"
            android:layout_marginTop="?attr/actionBarSize"
            android:layout_marginRight="4dp"
            android:layout_marginLeft="4dp"
            android:elevation="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/addButton"
            android:text="+"
            android:textSize="25sp"
            android:textColor="@android:color/white"
            android:textAlignment="center"
            android:layout_marginRight="15dp"
            android:layout_marginBottom="15dp"
            android:background="@drawable/circlebtn"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:stateListAnimator="@animator/anim"
            android:elevation="4dp"
            style="?android:buttonStyleSmall"
            android:layout_gravity="right|bottom" />
        

    </FrameLayout>

    <com.androprogrammer.test.materialapp1.ScrimInsetsFrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:elevation="8dp"
        android:layout_gravity="start"
        app:insetForeground="#4000">

        <ListView
            android:id="@+id/list_drawer"
            android:layout_width="@dimen/drawer_width"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"
            android:background="@android:color/white"
            android:fitsSystemWindows="true" />
       

    </com.androprogrammer.test.materialapp1.ScrimInsetsFrameLayout>
</android.support.v4.widget.DrawerLayout>
actionbarlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:minHeight="?attr/actionBarSize"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/toolbar"
    android:elevation="4dp"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    android:background="@color/primary"/>

drawerlist_header.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:id="@+id/imageView"
        android:scaleType="centerCrop"
        android:clickable="false"
        android:src="@drawable/headerbg"
        android:layout_gravity="center_horizontal|top" />
</FrameLayout>

Step -2 : Now layouts are set for main view. now we will create style for app so it will look same threw out the app.
values/styles.xml



    

    
values/attrs.xml

    
        
    

values/color.xml

    #fe2e2e
    #EFcb2424

values/dimens.xml

    2dp
    4dp
    320dp
    56dp


values/strings.xml


    My Application1
    Settings
    open
    close


values-v21/styles.xml


Step - 3 : Now we will move forward to code section of the app which was also know as dirty or magical section of the app.  put below code in ScrimInsetsFrameLayout classor what ever you named it in xml layout. 
ScrimInsetsFrameLayout.java

package com.androprogrammer.test.materialapp1;

/**
* 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.
*/

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.widget.FrameLayout;

/**
 * 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);
    }
}


It is copyrighted by Google developer so if you copy it please give proper reference to them.
MainActivity.java

package com.androprogrammer.test.materialapp1;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements ListView.OnItemClickListener
{
    private android.support.v7.widget.RecyclerView mRecyclerView;
    private android.support.v7.widget.RecyclerView.Adapter mAdapter;
    private android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
    private android.support.v7.widget.Toolbar toolbar;
    private android.support.v4.widget.DrawerLayout drawerLayout;
    private ActionBarDrawerToggle drawerToggle;
    private ListView leftDrawerList;
    private ArrayAdapter<string> navigationDrawerAdapter;
    private Button fab;
    private String[] leftSliderData = {"Home", "Android", "Sitemap", "About", "Contact Me"};
    protected String[] myDataset = {"facebook", "Google+", "twitter", "Linkdin", "pintrest"};


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

        setRef();

        //Set the custom toolbar
        if (toolbar != null)
        {
            toolbar.setTitle(R.string.app_name);
            setSupportActionBar(toolbar);
        }

        drawerToggle = new ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.open,
                R.string.close
        )

        {
            public void onDrawerClosed(View view)
            {
                Log.d("MainActivity", "OnDrawerClosed");
                super.onDrawerClosed(view);
                invalidateOptionsMenu();
            }

            public void onDrawerOpened(View drawerView)
            {
                Log.d("MainActivity", "OnDrawerOpened");
                super.onDrawerOpened(drawerView);
                invalidateOptionsMenu();
            }
        };

        drawerLayout.setDrawerListener(drawerToggle);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);

    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        drawerToggle.syncState();
    }

    private void setRef()
    {
        mRecyclerView = (android.support.v7.widget.RecyclerView) findViewById(R.id.my_recycler_view);
        fab = (Button) findViewById(R.id.addButton);
        drawerLayout = (android.support.v4.widget.DrawerLayout) findViewById(R.id.drawer_layout);
        drawerLayout.setStatusBarBackground(R.color.primarydark);

        toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar);
        leftDrawerList = (ListView) findViewById(R.id.list_drawer);

        View list_header = getLayoutInflater().inflate(R.layout.drawerlist_header, null);
        leftDrawerList.addHeaderView(list_header);
        navigationDrawerAdapter = new ArrayAdapter<string>(MainActivity.this, android.R.layout.simple_expandable_list_item_1, leftSliderData);
        leftDrawerList.setAdapter(navigationDrawerAdapter);
        leftDrawerList.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id)
    {
        Toast.makeText(getApplicationContext(), "Clicked on " + leftSliderData[position - 1], Toast.LENGTH_LONG).show();
    }

}


Screen Shots

android navigation drawer android navigation drawer

Step - 4 : Now every thing done but please apply theme in manifest.xml in application tag.
android:theme="@style/AppTheme"
And one more thing is that to run app you need to add libraries in app.gradle file it is in app folder named as build.gradle

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:support-v4:21.0.0'
    compile 'com.android.support:recyclerview-v7:21.0.+'
    compile 'com.android.support:appcompat-v7:21.0.2'



Well that's it from my side now run the app and check how it works. hope you learned from this tutorial if find it useful please hare it with your friends or in circle. if i miss any thing or you have any query please let me know in below comment box.

Keep coding...

13 comments :

  1. Replies
    1. Hey waqas i just added link to my github account. you can get source code files from there.

      Delete
    2. Thanks @Wasim
      btw, I am getting R error in Android studio, any help to fix this error?

      Delete
    3. can you tell me where you getting error ?
      error log or screen shot can be helpful.

      Delete
  2. Hey I got your point
    Actually I have changed package name so please change it and then rebuild the project.

    ReplyDelete
  3. Hi my friend, thanks very much... this is great but I have a doubt....

    How can I do to maintain my navigation drawer item grayed or colored to inform what option is selected.

    Something like youtube app.

    Thanks so much!

    ReplyDelete
    Replies
    1. To set selected you need to pass position like this

      leftDrawerList.setItemChecked(position, true);
      leftDrawerList.setSelection(position);


      one more thing if you are redirecting user to another activity then you need a global method which do same task for you.
      i have implemented this thing in base activity which set list view selection.

      Delete
  4. In gradel it shows "this support libarary should not be used a lowe version 21 and the target SDK version 23 "

    ReplyDelete
    Replies
    1. You can use newer version of gradle but this code will work.
      so don't worry about it.

      Delete
  5. Hi
    can you add Expandeble recylerview in navigation drawer

    ReplyDelete
    Replies
    1. Hi, Thanks for suggestion but sorry i can't. I am working on something else and don't get time to update this. but do some googling you will find lots of tutorials for it.

      Delete