본문 바로가기

나의 플랫폼/안드로이드

[Android][스크랩] How To Make Material Design Navigation Drawer With Header View

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
http://www.android4devs.com/2014/12/how-to-make-material-design-navigation-drawer.html


One of the most evident changes when the material design came out and I got my hands on was the new Navigation Drawer, The Hamburger toggle icon animation seemed very beautiful, The Overlapping drawer on status bar also seemed nice effect and so we are going to learn how to make a Navigation Drawer in this tutorial, with that effect and we are going to use the new RecyclerView for making the list view with the header (just like Gmail app's Navigation Drawer) in the sliding drawer, So let's begin.


Prerequisites

We are going to make the 

Navigation Drawer over the Action Bar/ App Bar we made in the previous post. It isn't completely necessary to read the previous post where I show how to make a material design App Bar but I strongly recommend you to read it once.

Requirements

1. Android Studio 1.0.1 (Latest at the time of writing this post)


2. CircleImage Library (which is used in the header view for the profile picture). Add this library in your gradel build by including the following dependency

compile 'de.hdodenhof:circleimageview:1.2.1'



3. Appcombat v7-21 Support Library As I have explained in my earlier post, If you are running over new Android studio you don't need to add a compiled dependency of appcombat v7 21 as the Android Studio takes care of it. The next thing you need to add in your gradel build is compiled dependency for RecyclerView. To do that add the following line in your build.gradel file.

compile 'com.android.support:recyclerview-v7:21.0.+' 


Let's Understand How the Navigation Drawer Works




To make a Navigation Drawer, you have to initiate it in the XML file of the activity on which you want to implement the material design sliding drawer. To initiate the Navigation Drawer we need to use the XML tag as shown below and as with any other view you have to set the layout attributes such as Height, Width, and Id etc. Drawer Layout should have two child views

First Child - Main Content
Second Child - Sliding Drawer Content

For Example look at the structure below
<android.support.v4.widget.DrawerLayout>

<RelativeLayout></RelativeLayout> - Main Content (First Child)
 <RecyclerView></RecyclerView> - Sliding Drawer Content (Second Child)

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

Now you have to decide from which side of the app you want your Navigation Drawer to slid in the layout, Depending on your choice set the layout_gravityt of the child view to either left if you want the drawer to slide from left or right if you want it to slide from right.

Steps To Make A Material Design Navigation Drawer 

First let's see what we are trying to make



As you can see we have a sliding drawer which slides from the left side of the app and overlaps the translucent status bar, inside the drawer we have a header view which contains a circular view for the profile picture and two Text Views, below that there is our List view with icons.

As we are making a navigation drawer over the Action Bar/App Bar we made in the earlier post what we have with us while we start out, looks something like this

app bar

Just a App Bar with icons on it as we made this in my previous post

Now lets start to code our Navigation Drawer


1. First open up our activity_main.xml file from res folder and add a DrawerLayout as the root view of the app and inside the drawer layout add the App bar which is done by adding the include tag inside the first child of your drawer layout (in my case its the linear layout) now as the drawer layout is your root layout and you have included the App bar inside the drawers first child, all this makes sure the drawer slides over the App bar as we wanted. You would get the clear understanding after you see the layout file.  

This is how the activity_main.xml looks like



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="utf-8"?>
 
<android.support.v4.widget.DrawerLayout
 
    android:id="@+id/DrawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:elevation="7dp">
 
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
 
 
        <include
            android:id="@+id/tool_bar"
            layout="@layout/tool_bar">
        </include>
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World" />
 
    </LinearLayout>
 
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/RecyclerView"
        android:layout_width="320dp"
        android:layout_height="match_parent"
        android:layout_gravity="left"
 
        android:background="#ffffff"
        android:scrollbars="vertical">
 
    </android.support.v7.widget.RecyclerView>
 
 
</android.support.v4.widget.DrawerLayout>



2. Now let's define how a single row in the navigation drawer list going to look like, For that make a new layout file by going to res=>layouts and make a new layout file and name it item_row.xml. Now as we want to have a icon and a title in in every row of our list we would put an image view and TextView inside a RelativeLayout.

Here is how the item_row.xml looks like



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:paddingTop="8dp"
    android:paddingBottom="8dp"
    android:layout_height="wrap_content"
    android:background="#ffffff"
    android:orientation="horizontal">
 
 
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/rowIcon"
        android:paddingLeft="16dp"
        android:src="@drawable/ic_launcher"/>
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="12dp"
        android:paddingTop="4dp"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/rowText" />
 
</LinearLayout>


3. Now we need to define our Header View and for that we make a new layout file by going to  res=>layouts and create a new layout file, name the new layout file asheader.xml. Now our header view has one circular image view for profile picture and two text views, adding text view is easy and to add circular profile picture I have used a library called CircleImageView as i have told earlier, and finally to make things look good I have given a background image to the header view. for better understanding look at the layout file

This is how the header.xml looks like.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="178dp"
    android:background="@drawable/background_poly"
    android:orientation="vertical"
    android:weightSum="1">
 
 
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:orientation="vertical"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">
 
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:textColor="#ffffff"
            android:text="Akash Bangad"
            android:textSize="14sp"
            android:textStyle="bold"
 
            />
 
        <TextView
            android:id="@+id/email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#ffffff"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="5dp"
            android:text="akash.bangad93@gmail.com"
            android:textSize="14sp"
            android:textStyle="normal"
 
            />
    </LinearLayout>
 
    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/aka"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="38dp"
        android:id="@+id/circleView"
       />
</RelativeLayout>

4. The next step is to put icons and images in our projects Drawables folder, As always i have downloaded icons from www.icons4android.com which is one of the 5 tools I recommend everyone to use, Copied those icons and pasted them in drawable folder inside my project. I have also added two pictures to the project one is for the header background and other is for the profile picture.

5. Now we have all the layouts ready, We have to initiate everything in code now. So let's look at MainActivity.java I have put up the comments to help you understand what's going on 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package com.android4devs.navigationdrawer;
 
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
 
 
public class MainActivity extends ActionBarActivity {
 
    //First We Declare Titles And Icons For Our Navigation Drawer List View
    //This Icons And Titles Are holded in an Array as you can see
 
    String TITLES[] = {"Home","Events","Mail","Shop","Travel"};
    int ICONS[] = {R.drawable.ic_home,R.drawable.ic_events,R.drawable.ic_mail,R.drawable.ic_shop,R.drawable.ic_travel};
 
    //Similarly we Create a String Resource for the name and email in the header view
    //And we also create a int resource for profile picture in the header view
 
    String NAME = "Akash Bangad";
    String EMAIL = "akash.bangad@android4devs.com";
    int PROFILE = R.drawable.aka;
 
    private Toolbar toolbar;                              // Declaring the Toolbar Object
 
    RecyclerView mRecyclerView;                           // Declaring RecyclerView
    RecyclerView.Adapter mAdapter;                        // Declaring Adapter For Recycler View
    RecyclerView.LayoutManager mLayoutManager;            // Declaring Layout Manager as a linear layout manager
    DrawerLayout Drawer;                                  // Declaring DrawerLayout
 
    ActionBarDrawerToggle mDrawerToggle;                  // Declaring Action Bar Drawer Toggle
 
 
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
    /* Assinging the toolbar object ot the view
    and setting the the Action bar to our toolbar
     */
    toolbar = (Toolbar) findViewById(R.id.tool_bar);
    setSupportActionBar(toolbar);
 
 
 
 
        mRecyclerView = (RecyclerView) findViewById(R.id.RecyclerView); // Assigning the RecyclerView Object to the xml View
 
        mRecyclerView.setHasFixedSize(true);                            // Letting the system know that the list objects are of fixed size
 
        mAdapter = new MyAdapter(TITLES,ICONS,NAME,EMAIL,PROFILE);       // Creating the Adapter of MyAdapter class(which we are going to see in a bit)
                                                                         // And passing the titles,icons,header view name, header view email,
                                                                         // and header view profile picture
 
        mRecyclerView.setAdapter(mAdapter);                              // Setting the adapter to RecyclerView
 
        mLayoutManager = new LinearLayoutManager(this);                 // Creating a layout Manager
 
        mRecyclerView.setLayoutManager(mLayoutManager);                 // Setting the layout Manager
 
 
        Drawer = (DrawerLayout) findViewById(R.id.DrawerLayout);        // Drawer object Assigned to the view
        mDrawerToggle = new ActionBarDrawerToggle(this,Drawer,toolbar,R.string.openDrawer,R.string.closeDrawer){
 
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                // code here will execute once the drawer is opened( As I dont want anything happened whe drawer is
                // open I am not going to put anything here)
            }
 
            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                // Code here will execute once drawer is closed
            }
 
 
 
    }; // Drawer Toggle Object Made
        Drawer.setDrawerListener(mDrawerToggle); // Drawer Listener set to the Drawer toggle
        mDrawerToggle.syncState();               // Finally we set the drawer toggle sync State
 
    }
 
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
 
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
 
        return super.onOptionsItemSelected(item);
    }
}



6. Now we have to make the adapter fot the header view and list view in our drawer, earlier adding headers was as easy as calling addHeaderView() function from the object of list view but with recycler view things have changed and now we have to make changes to our adapter to make it understand that we want the first row of the list to be our header, we do that by inflating the header.xml for the first row and item_row.xml for the rest of the rows. look at the code where I have explained the process with appropriate comments.

So this is how the MyAdapter.java looks like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.android4devs.navigationdrawer;
 
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
/**
 * Created by hp1 on 28-12-2014.
 */
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
     
    private static final int TYPE_HEADER = 0;  // Declaring Variable to Understand which View is being worked on
                                               // IF the view under inflation and population is header or Item
    private static final int TYPE_ITEM = 1;
 
    private String mNavTitles[]; // String Array to store the passed titles Value from MainActivity.java
    private int mIcons[];       // Int Array to store the passed icons resource value from MainActivity.java
     
    private String name;        //String Resource for header View Name
    private int profile;        //int Resource for header view profile picture
    private String email;       //String Resource for header view email 
 
 
    // Creating a ViewHolder which extends the RecyclerView View Holder
    // ViewHolder are used to to store the inflated views in order to recycle them
 
    public static class ViewHolder extends RecyclerView.ViewHolder {
        int Holderid;      
         
        TextView textView; 
        ImageView imageView;
        ImageView profile;
        TextView Name;
        TextView email;
        
 
        public ViewHolder(View itemView,int ViewType) {                 // Creating ViewHolder Constructor with View and viewType As a parameter
            super(itemView);
             
            
            // Here we set the appropriate view in accordance with the the view type as passed when the holder object is created
             
            if(ViewType == TYPE_ITEM) {
                textView = (TextView) itemView.findViewById(R.id.rowText); // Creating TextView object with the id of textView from item_row.xml
                imageView = (ImageView) itemView.findViewById(R.id.rowIcon);// Creating ImageView object with the id of ImageView from item_row.xml
                Holderid = 1;                                               // setting holder id as 1 as the object being populated are of type item row
            }
            else{
 
 
                Name = (TextView) itemView.findViewById(R.id.name);         // Creating Text View object from header.xml for name
                email = (TextView) itemView.findViewById(R.id.email);       // Creating Text View object from header.xml for email
                profile = (ImageView) itemView.findViewById(R.id.circleView);// Creating Image view object from header.xml for profile pic
                Holderid = 0;                                                // Setting holder id = 0 as the object being populated are of type header view
            }
        }
 
         
    }
 
 
 
    MyAdapter(String Titles[],int Icons[],String Name,String Email, int Profile){ // MyAdapter Constructor with titles and icons parameter
                                            // titles, icons, name, email, profile pic are passed from the main activity as we
        mNavTitles = Titles;                //have seen earlier
        mIcons = Icons;
        name = Name;
        email = Email;
        profile = Profile;                     //here we assign those passed values to the values we declared here
        //in adapter
 
 
 
    }
 
 
 
    //Below first we ovverride the method onCreateViewHolder which is called when the ViewHolder is
    //Created, In this method we inflate the item_row.xml layout if the viewType is Type_ITEM or else we inflate header.xml
    // if the viewType is TYPE_HEADER
    // and pass it to the view holder
 
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 
        if (viewType == TYPE_ITEM) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row,parent,false); //Inflating the layout
 
            ViewHolder vhItem = new ViewHolder(v,viewType); //Creating ViewHolder and passing the object of type view
 
            return vhItem; // Returning the created object
 
            //inflate your layout and pass it to view holder
 
        else if (viewType == TYPE_HEADER) {
 
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header,parent,false); //Inflating the layout
 
            ViewHolder vhHeader = new ViewHolder(v,viewType); //Creating ViewHolder and passing the object of type view
 
            return vhHeader; //returning the object created
 
             
        }
        return null;
 
    }
 
    //Next we override a method which is called when the item in a row is needed to be displayed, here the int position
    // Tells us item at which position is being constructed to be displayed and the holder id of the holder object tell us
    // which view type is being created 1 for item row
    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
        if(holder.Holderid ==1) {                              // as the list view is going to be called after the header view so we decrement the
                                                              // position by 1 and pass it to the holder while setting the text and image
            holder.textView.setText(mNavTitles[position - 1]); // Setting the Text with the array of our Titles
            holder.imageView.setImageResource(mIcons[position -1]);// Settimg the image with array of our icons
        }
        else{
 
            holder.profile.setImageResource(profile);           // Similarly we set the resources for header view
            holder.Name.setText(name);
            holder.email.setText(email);
        }
    }
 
    // This method returns the number of items present in the list
    @Override
    public int getItemCount() {
        return mNavTitles.length+1// the number of items in the list will be +1 the titles including the header view.
    }
 
     
    // Witht the following method we check what type of view is being passed
    @Override
    public int getItemViewType(int position) { 
        if (isPositionHeader(position))
            return TYPE_HEADER;
 
        return TYPE_ITEM;
    }
 
    private boolean isPositionHeader(int position) {
        return position == 0;
    }
 
}



Now if you run the app this is what you would see




As you can see the drawer is overlapping the Toolbar/ Action bar, If you are happy with this design and pattern then you can consider the project done, as this pattern is also one of the material design patterns used by many official Google apps and other apps as well, now if you want to make the status bar translucent and have your drawer slide under the status bar then you should follow along

Before we get to making the status bar translucent and having our drawer slide below it I must tell you the method you are going to see isnt exactly the right way to implement the above effect as if we want the drawer to slide below the status bar we have to make the status bar translucent and once we make it translucent we can not assign any color to the status bar. You would understand what I am talking about in just a minute

First let me tell you that the status bar translucent method is not available for pre kitkat devices, so we make two styles.xml files one for version 19 api (kitkat) and one for Version 21 api (Lollipop). so in all we have three styles.xml files.

we now add the following styles to both our styles.xml files i.e style for kitkat and lollipop
here is how the style.xml file look for both the files.

1
 
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar" >
        <item name="android:windowTranslucentStatus">true</item>
    </style>
 
</resources>

Now if you look at the app this is what you are going to see


As you can see we have got the translucent status bar but the toolbar got below it and if you add the android:fitSystemWindows="true"  tag in your activity_main.xml as a property to the root layout( DrawerLayout) file the toolbar will move down and adjust to the status bar but even if we get the toolbar down we won't be able to change the color of the status bar once it is set to translucent. now as i have earlier said this is not the exact method to do this, if you look at the stack overflow discussion here you could find that to achieve the above effect you have to use scrimInsetFrameLayout which i dont belive is really very complicated for this effect

So what I do is I have added a padding of 24dp to the tool_bar.xml and as I only want the padding to take effect on kitkat and lollipop devices I have made two dimens files with version api set to 19 and 21 for kitkat  and lollipop respectevely and for the pre lollipop devices I have set 0dp padding in the 3rd dimens file, then I have added the padding top to tool_bar.xml

so finally this is how tool_bar.xml looks
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/ColorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:elevation="4dp"
    android:paddingTop="@dimen/tool_bar_top_padding"
 
 
    >
 
</android.support.v7.widget.Toolbar>



Now everything is done and set and if you open the app this is what you would get




Perfect it looks just the way wanted it to be, So that's how you make a Material design Navigation Drawer which has a Hamburger Toggle icons animating into arrow. If you liked this post please comment and share the post on your social networks.

EDIT: head over to Recycler View : Handling OnItemTouch For Navigation Drawer post if you want to learn how to handle the click events for navigation drawer.