blob: f7f7916651625b26f27b5cde7aaa8d27efd2ce51 [file] [log] [blame]
Scott Main7c840552013-03-26 18:53:01 -07001page.title=Creating a Navigation Drawer
Scott Main0c391522013-05-15 14:38:16 -07002page.tags="DrawerLayout", "navigation"
Scott Main7c840552013-03-26 18:53:01 -07003
4trainingnavtop=true
5
6@jd:body
7
8<div id="tb-wrapper">
9<div id="tb">
10
11<h2>This lesson teaches you to:</h2>
12<ol>
13 <li><a href="#DrawerLayout">Create a Drawer Layout</a></li>
14 <li><a href="#Init">Initialize the Drawer List</a></li>
15 <li><a href="#ListItemClicks">Handle Navigation Click Events</a></li>
16 <li><a href="#OpenClose">Listen for Open and Close Events</a></li>
17 <li><a href="#ActionBarIcon">Open and Close with the App Icon</a></li>
18</ol>
19
20<h2>Try it out</h2>
21
22<div class="download-box">
23<a href="http://developer.android.com/shareables/training/NavigationDrawer.zip"
24 class="button">Download the sample app</a>
25<p class="filename">NavigationDrawer.zip</p>
26</div>
27
Scott Main67823512013-05-16 10:41:15 -070028<div class="download-box">
Scott Main4c9ed632013-09-26 12:39:27 -070029<a href="http://developer.android.com/downloads/design/Android_Design_Icons_20130926.zip"
30 class="button">Download the Action Bar Icon Pack</a>
31<p class="filename">Android_Design_Icons_20130926.zip</p>
Scott Main67823512013-05-16 10:41:15 -070032</div>
33
Scott Main7c840552013-03-26 18:53:01 -070034</div>
35</div>
36
37
38
39<p>The navigation drawer is a panel that displays the app’s main navigation options
40on the left edge of the screen. It is hidden most of the time, but is revealed
41when the user swipes a finger from the left edge of the screen or, while at the top level of the
42app, the user touches the app icon in the action bar.</p>
43
44<p>This lesson describes how to implement a navigation drawer using the
45{@link android.support.v4.widget.DrawerLayout} APIs available in the
Scott Main4e2c9dc2013-07-23 19:35:17 -070046<a href="{@docRoot}tools/support-library/index.html">Support Library</a>.</p>
Scott Main7c840552013-03-26 18:53:01 -070047
48<div class="note design">
49<p><strong>Navigation Drawer Design</strong></p>
50<p>Before you decide to use a navigation drawer in your app, you should understand the use
51cases and design principles defined in the
Scott Main62cd3ec2013-05-13 16:27:10 -070052<a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation Drawer</a> design guide.</p>
Scott Main7c840552013-03-26 18:53:01 -070053</div>
54
55
56<h2 id="DrawerLayout">Create a Drawer Layout</h2>
57
58<p>To add a navigation drawer, declare your user interface with a
59{@link android.support.v4.widget.DrawerLayout} object as the root view of your layout.
60Inside the {@link android.support.v4.widget.DrawerLayout}, add one view that contains
61the main content for the screen (your primary layout when the drawer is hidden) and another view
62that contains the contents of the navigation drawer.</p>
63
64<p>For example, the following layout uses a {@link
65android.support.v4.widget.DrawerLayout} with two child views: a {@link android.widget.FrameLayout}
66to contain the main content (populated by a {@link android.app.Fragment} at
67runtime), and a {@link android.widget.ListView} for the navigation drawer.</p>
68
69<pre>
70&lt;android.support.v4.widget.DrawerLayout
71 xmlns:android="http://schemas.android.com/apk/res/android"
72 android:id="@+id/drawer_layout"
73 android:layout_width="match_parent"
74 android:layout_height="match_parent">
75 &lt;!-- The main content view -->
76 &lt;FrameLayout
77 android:id="@+id/content_frame"
78 android:layout_width="match_parent"
79 android:layout_height="match_parent" />
80 &lt;!-- The navigation drawer -->
81 &lt;ListView android:id="@+id/left_drawer"
82 android:layout_width="240dp"
83 android:layout_height="match_parent"
84 android:layout_gravity="start"
85 android:choiceMode="singleChoice"
86 android:divider="&#64;android:color/transparent"
87 android:dividerHeight="0dp"
88 android:background="#111"/>
89&lt;/android.support.v4.widget.DrawerLayout>
90</pre>
91
92<p>This layout demonstrates some important layout characteristics:</p>
93<ul>
94 <li>The main content view (the {@link android.widget.FrameLayout} above)
95 <strong>must be the first child</strong> in the {@link
96 android.support.v4.widget.DrawerLayout} because the XML order implies z-ordering
97 and the drawer must be on top of the content.</li>
98 <li>The main content view is set to match the parent
99 view's width and height, because it represents the entire UI when the
100 navigation drawer is hidden.</li>
101 <li>The drawer view (the {@link android.widget.ListView}) <strong>must specify its horizontal
102 gravity</strong> with the {@code android:layout_gravity} attribute. To
103 support right-to-left (RTL) languages, specify the value with {@code "start"}
104 instead of {@code "left"} (so the drawer appears on the right when the layout is RTL).</p>
105 </li>
106 <li>The drawer view specifies its width in {@code dp} units and the height matches the parent
107 view. The drawer width should be no more than 320dp so the user can always
108 see a portion of the main content.</li>
109</ul>
110
111
112
113<h2 id="Init">Initialize the Drawer List</h2>
114
115<p>In your activity, one of the first things to do is initialize
116the navigation drawer's list of items. How you do so depends on the content of your app, but
117a navigation drawer often consists of a {@link android.widget.ListView}, so the list
118should be populated by an {@link android.widget.Adapter} (such as {@link
119android.widget.ArrayAdapter} or {@link android.widget.SimpleCursorAdapter}).</p>
120
121<p>For example, here's how you can initialize the navigation list with a
122<a href="{@docRoot}guide/topics/resources/string-resource.html#StringArray">string array</a>:</p>
123
124<pre>
125public class MainActivity extends Activity {
126 private String[] mPlanetTitles;
Scott Maind25cc952013-07-18 10:18:57 -0700127 private DrawerLayout mDrawerLayout;
Scott Main7c840552013-03-26 18:53:01 -0700128 private ListView mDrawerList;
129 ...
130
131 &#64;Override
132 public void onCreate(Bundle savedInstanceState) {
133 super.onCreate(savedInstanceState);
134 setContentView(R.layout.activity_main);
135
136 mPlanetTitles = getResources().getStringArray(R.array.planets_array);
Scott Maind25cc952013-07-18 10:18:57 -0700137 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Scott Main7c840552013-03-26 18:53:01 -0700138 mDrawerList = (ListView) findViewById(R.id.left_drawer);
139
140 // Set the adapter for the list view
141 mDrawerList.setAdapter(new ArrayAdapter&lt;String>(this,
142 R.layout.drawer_list_item, mPlanetTitles));
143 // Set the list's click listener
144 mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
145
146 ...
147 }
148}
149</pre>
150
151<p>This code also calls {@link android.widget.ListView#setOnItemClickListener
152setOnItemClickListener()} to receive click events in the navigation drawer's list.
153The next section shows how to implement this interface
154and change the content view when the user selects an item.</p>
155
156
157
158<h2 id="ListItemClicks">Handle Navigation Click Events</h2>
159
160<p>When the user selects an item in the drawer's list, the system calls {@link
161android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()} on the
162{@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} given to
163{@link android.widget.ListView#setOnItemClickListener setOnItemClickListener()}.</p>
164
165<p>What you do in the {@link
166android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()} method
167depends on how you've implemented your <a
168href="{@docRoot}design/patterns/app-structure.html">app structure</a>. In the following example,
169selecting each item in the list inserts a different {@link
170android.app.Fragment} into the main content view (the
171{@link android.widget.FrameLayout} element identified by the {@code R.id.content_frame} ID):</p>
172
173<pre>
174private class DrawerItemClickListener implements ListView.OnItemClickListener {
175 &#64;Override
176 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
177 selectItem(position);
178 }
179}
180
181/** Swaps fragments in the main content view */
182private void selectItem(int position) {
183 // Create a new fragment and specify the planet to show based on position
184 Fragment fragment = new PlanetFragment();
185 Bundle args = new Bundle();
186 args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
187 fragment.setArguments(args);
188
189 // Insert the fragment by replacing any existing fragment
190 FragmentManager fragmentManager = getFragmentManager();
191 fragmentManager.beginTransaction()
192 .replace(R.id.content_frame, fragment)
193 .commit();
194
195 // Highlight the selected item, update the title, and close the drawer
Scott Maind25cc952013-07-18 10:18:57 -0700196 mDrawerList.setItemChecked(position, true);
Scott Main7c840552013-03-26 18:53:01 -0700197 setTitle(mPlanetTitles[position]);
Scott Maind25cc952013-07-18 10:18:57 -0700198 mDrawerLayout.closeDrawer(mDrawerList);
Scott Main7c840552013-03-26 18:53:01 -0700199}
200
201&#64;Override
202public void setTitle(CharSequence title) {
203 mTitle = title;
204 getActionBar().setTitle(mTitle);
205}
206
207</pre>
208
209
210
211
212<h2 id="OpenClose">Listen for Open and Close Events</h2>
213
214<p>To listen for drawer open and close events, call {@link
215android.support.v4.widget.DrawerLayout#setDrawerListener setDrawerListener()} on your
216{@link android.support.v4.widget.DrawerLayout} and pass it an implementation of
217{@link android.support.v4.widget.DrawerLayout.DrawerListener}. This interface provides callbacks
218for drawer events such as {@link
219android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerOpened onDrawerOpened()} and {@link
220android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerClosed onDrawerClosed()}.</p>
221
222<p>However, rather than implementing the {@link
223android.support.v4.widget.DrawerLayout.DrawerListener}, if your activity includes the
224<a href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, you can instead
225extend the {@link android.support.v4.app.ActionBarDrawerToggle} class. The
226{@link android.support.v4.app.ActionBarDrawerToggle} implements
227{@link android.support.v4.widget.DrawerLayout.DrawerListener} so you can still override those
228callbacks, but it also facilitates the proper
229interaction behavior between the action bar icon and the navigation drawer (discussed further in
230the next section).</p>
231
Scott Main62cd3ec2013-05-13 16:27:10 -0700232<p>As discussed in the <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation
Scott Main7c840552013-03-26 18:53:01 -0700233Drawer</a> design guide, you should modify the contents of the action bar
234when the drawer is visible, such as to change the title and remove action items that are
235contextual to the main content. The following code shows how you can do so by overriding {@link
236android.support.v4.widget.DrawerLayout.DrawerListener} callback methods with an instance
237of the {@link android.support.v4.app.ActionBarDrawerToggle} class:</p>
238
239<pre>
240public class MainActivity extends Activity {
241 private DrawerLayout mDrawerLayout;
242 private ActionBarDrawerToggle mDrawerToggle;
243 private CharSequence mDrawerTitle;
244 private CharSequence mTitle;
245 ...
246
247 &#64;Override
248 public void onCreate(Bundle savedInstanceState) {
249 super.onCreate(savedInstanceState);
250 setContentView(R.layout.activity_main);
251 ...
252
253 mTitle = mDrawerTitle = getTitle();
254 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
255 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
256 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {
257
258 /** Called when a drawer has settled in a completely closed state. */
259 public void onDrawerClosed(View view) {
Ricardo Cerverab96c0ab2014-01-15 15:16:30 -0800260 super.onDrawerClosed(view);
Scott Main7c840552013-03-26 18:53:01 -0700261 getActionBar().setTitle(mTitle);
262 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
263 }
264
265 /** Called when a drawer has settled in a completely open state. */
266 public void onDrawerOpened(View drawerView) {
Ricardo Cerverab96c0ab2014-01-15 15:16:30 -0800267 super.onDrawerOpened(drawerView);
Scott Main7c840552013-03-26 18:53:01 -0700268 getActionBar().setTitle(mDrawerTitle);
269 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
270 }
271 };
272
273 // Set the drawer toggle as the DrawerListener
274 mDrawerLayout.setDrawerListener(mDrawerToggle);
275 }
276
277 /* Called whenever we call invalidateOptionsMenu() */
278 &#64;Override
279 public boolean onPrepareOptionsMenu(Menu menu) {
280 // If the nav drawer is open, hide action items related to the content view
281 boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
282 menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
283 return super.onPrepareOptionsMenu(menu);
284 }
285}
286</pre>
287
288<p>The next section describes the {@link android.support.v4.app.ActionBarDrawerToggle} constructor
289arguments and the other steps required to set it up to handle interaction with the
290action bar icon.</p>
291
292
293
294<h2 id="ActionBarIcon">Open and Close with the App Icon</h2>
295
296<p>Users can open and close the navigation drawer with a swipe gesture from or towards the left
297edge of the screen, but if you're using the <a
298href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, you should also allow users to
299open and close it by touching the app icon. And the app icon should also indicate the presence of
300the navigation drawer with a special icon. You can implement all this behavior by using the
301{@link android.support.v4.app.ActionBarDrawerToggle} shown in the previous section.</p>
302
303<p>To make {@link android.support.v4.app.ActionBarDrawerToggle} work, create an instance of
304it with its constructor, which requires the following arguments:</p>
305<ul>
306 <li>The {@link android.app.Activity} hosting the drawer.
307 <li>The {@link android.support.v4.widget.DrawerLayout}.
308 <li>A drawable resource to use as the drawer indicator.
Scott Main4c9ed632013-09-26 12:39:27 -0700309 <p>The standard navigation drawer icon is available in the <a href="http://developer.android.com/downloads/design/Android_Design_Icons_20130926.zip"
310>Download the Action Bar Icon Pack</a>.</p>
Scott Main7c840552013-03-26 18:53:01 -0700311 <li>A String resource to describe the "open drawer" action (for accessibility).
312 <li>A String resource to describe the "close drawer" action (for accessibility).
313</ul>
314
315<p>Then, whether or not you've created a subclass of
316{@link android.support.v4.app.ActionBarDrawerToggle} as your drawer listener, you need to call
317upon your {@link android.support.v4.app.ActionBarDrawerToggle} in a few places throughout your
318activity lifecycle:</p>
319
320<pre>
321public class MainActivity extends Activity {
322 private DrawerLayout mDrawerLayout;
323 private ActionBarDrawerToggle mDrawerToggle;
324 ...
325
326 public void onCreate(Bundle savedInstanceState) {
327 ...
328
329 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Scott Main67823512013-05-16 10:41:15 -0700330 mDrawerToggle = new ActionBarDrawerToggle(
331 this, /* host Activity */
332 mDrawerLayout, /* DrawerLayout object */
333 R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
334 R.string.drawer_open, /* "open drawer" description */
335 R.string.drawer_close /* "close drawer" description */
336 ) {
Scott Main7c840552013-03-26 18:53:01 -0700337
338 /** Called when a drawer has settled in a completely closed state. */
339 public void onDrawerClosed(View view) {
Ricardo Cerverab96c0ab2014-01-15 15:16:30 -0800340 super.onDrawerClosed(view);
Scott Main7c840552013-03-26 18:53:01 -0700341 getActionBar().setTitle(mTitle);
342 }
343
344 /** Called when a drawer has settled in a completely open state. */
345 public void onDrawerOpened(View drawerView) {
Ricardo Cerverab96c0ab2014-01-15 15:16:30 -0800346 super.onDrawerOpened(drawerView);
Scott Main7c840552013-03-26 18:53:01 -0700347 getActionBar().setTitle(mDrawerTitle);
348 }
349 };
350
351 // Set the drawer toggle as the DrawerListener
352 mDrawerLayout.setDrawerListener(mDrawerToggle);
353
354 getActionBar().setDisplayHomeAsUpEnabled(true);
355 getActionBar().setHomeButtonEnabled(true);
356 }
357
358 &#64;Override
359 protected void onPostCreate(Bundle savedInstanceState) {
360 super.onPostCreate(savedInstanceState);
361 // Sync the toggle state after onRestoreInstanceState has occurred.
362 mDrawerToggle.syncState();
363 }
364
365 &#64;Override
366 public void onConfigurationChanged(Configuration newConfig) {
367 super.onConfigurationChanged(newConfig);
368 mDrawerToggle.onConfigurationChanged(newConfig);
369 }
370
371 &#64;Override
372 public boolean onOptionsItemSelected(MenuItem item) {
373 // Pass the event to ActionBarDrawerToggle, if it returns
374 // true, then it has handled the app icon touch event
375 if (mDrawerToggle.onOptionsItemSelected(item)) {
376 return true;
377 }
378 // Handle your other action bar items...
379
380 return super.onOptionsItemSelected(item);
381 }
382
383 ...
384}
385</pre>
386
387<p>For a complete example of a navigation drawer, download the sample available at the
Scott Main0c391522013-05-15 14:38:16 -0700388<a href="#top">top of the page</a>.</p>