1 page.title=Creating a Navigation Drawer 2 page.tags="DrawerLayout", "navigation" 3 4 trainingnavtop=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 28 <div class="download-box"> 29 <a href="http://developer.android.com/downloads/design/Android_Navigation_Drawer_Icon_20130516.zip" 30 class="button">Download the nav drawer icons</a> 31 <p class="filename">Android_Navigation_Drawer_Icon_20130516.zip</p> 32 </div> 33 34 </div> 35 </div> 36 37 38 39 <p>The navigation drawer is a panel that displays the apps main navigation options 40 on the left edge of the screen. It is hidden most of the time, but is revealed 41 when the user swipes a finger from the left edge of the screen or, while at the top level of the 42 app, 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 46 <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>.</p> 47 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 51 cases and design principles defined in the 52 <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation Drawer</a> design guide.</p> 53 </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. 60 Inside the {@link android.support.v4.widget.DrawerLayout}, add one view that contains 61 the main content for the screen (your primary layout when the drawer is hidden) and another view 62 that contains the contents of the navigation drawer.</p> 63 64 <p>For example, the following layout uses a {@link 65 android.support.v4.widget.DrawerLayout} with two child views: a {@link android.widget.FrameLayout} 66 to contain the main content (populated by a {@link android.app.Fragment} at 67 runtime), and a {@link android.widget.ListView} for the navigation drawer.</p> 68 69 <pre> 70 <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 <!-- The main content view --> 76 <FrameLayout 77 android:id="@+id/content_frame" 78 android:layout_width="match_parent" 79 android:layout_height="match_parent" /> 80 <!-- The navigation drawer --> 81 <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="@android:color/transparent" 87 android:dividerHeight="0dp" 88 android:background="#111"/> 89 </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 116 the navigation drawer's list of items. How you do so depends on the content of your app, but 117 a navigation drawer often consists of a {@link android.widget.ListView}, so the list 118 should be populated by an {@link android.widget.Adapter} (such as {@link 119 android.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> 125 public class MainActivity extends Activity { 126 private String[] mPlanetTitles; 127 private DrawerLayout mDrawerLayout; 128 private ListView mDrawerList; 129 ... 130 131 @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); 137 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 138 mDrawerList = (ListView) findViewById(R.id.left_drawer); 139 140 // Set the adapter for the list view 141 mDrawerList.setAdapter(new ArrayAdapter<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 152 setOnItemClickListener()} to receive click events in the navigation drawer's list. 153 The next section shows how to implement this interface 154 and 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 161 android.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 166 android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()} method 167 depends on how you've implemented your <a 168 href="{@docRoot}design/patterns/app-structure.html">app structure</a>. In the following example, 169 selecting each item in the list inserts a different {@link 170 android.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> 174 private class DrawerItemClickListener implements ListView.OnItemClickListener { 175 @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 */ 182 private 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 196 mDrawerList.setItemChecked(position, true); 197 setTitle(mPlanetTitles[position]); 198 mDrawerLayout.closeDrawer(mDrawerList); 199 } 200 201 @Override 202 public 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 215 android.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 218 for drawer events such as {@link 219 android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerOpened onDrawerOpened()} and {@link 220 android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerClosed onDrawerClosed()}.</p> 221 222 <p>However, rather than implementing the {@link 223 android.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 225 extend 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 228 callbacks, but it also facilitates the proper 229 interaction behavior between the action bar icon and the navigation drawer (discussed further in 230 the next section).</p> 231 232 <p>As discussed in the <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation 233 Drawer</a> design guide, you should modify the contents of the action bar 234 when the drawer is visible, such as to change the title and remove action items that are 235 contextual to the main content. The following code shows how you can do so by overriding {@link 236 android.support.v4.widget.DrawerLayout.DrawerListener} callback methods with an instance 237 of the {@link android.support.v4.app.ActionBarDrawerToggle} class:</p> 238 239 <pre> 240 public class MainActivity extends Activity { 241 private DrawerLayout mDrawerLayout; 242 private ActionBarDrawerToggle mDrawerToggle; 243 private CharSequence mDrawerTitle; 244 private CharSequence mTitle; 245 ... 246 247 @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) { 260 getActionBar().setTitle(mTitle); 261 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() 262 } 263 264 /** Called when a drawer has settled in a completely open state. */ 265 public void onDrawerOpened(View drawerView) { 266 getActionBar().setTitle(mDrawerTitle); 267 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() 268 } 269 }; 270 271 // Set the drawer toggle as the DrawerListener 272 mDrawerLayout.setDrawerListener(mDrawerToggle); 273 } 274 275 /* Called whenever we call invalidateOptionsMenu() */ 276 @Override 277 public boolean onPrepareOptionsMenu(Menu menu) { 278 // If the nav drawer is open, hide action items related to the content view 279 boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); 280 menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); 281 return super.onPrepareOptionsMenu(menu); 282 } 283 } 284 </pre> 285 286 <p>The next section describes the {@link android.support.v4.app.ActionBarDrawerToggle} constructor 287 arguments and the other steps required to set it up to handle interaction with the 288 action bar icon.</p> 289 290 291 292 <h2 id="ActionBarIcon">Open and Close with the App Icon</h2> 293 294 <p>Users can open and close the navigation drawer with a swipe gesture from or towards the left 295 edge of the screen, but if you're using the <a 296 href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, you should also allow users to 297 open and close it by touching the app icon. And the app icon should also indicate the presence of 298 the navigation drawer with a special icon. You can implement all this behavior by using the 299 {@link android.support.v4.app.ActionBarDrawerToggle} shown in the previous section.</p> 300 301 <p>To make {@link android.support.v4.app.ActionBarDrawerToggle} work, create an instance of 302 it with its constructor, which requires the following arguments:</p> 303 <ul> 304 <li>The {@link android.app.Activity} hosting the drawer. 305 <li>The {@link android.support.v4.widget.DrawerLayout}. 306 <li>A drawable resource to use as the drawer indicator. 307 <p><a href="http://developer.android.com/downloads/design/Android_Navigation_Drawer_Icon_20130516.zip" 308 >Download the standard navigation icons</a> (available for both dark and light themes).</p> 309 <li>A String resource to describe the "open drawer" action (for accessibility). 310 <li>A String resource to describe the "close drawer" action (for accessibility). 311 </ul> 312 313 <p>Then, whether or not you've created a subclass of 314 {@link android.support.v4.app.ActionBarDrawerToggle} as your drawer listener, you need to call 315 upon your {@link android.support.v4.app.ActionBarDrawerToggle} in a few places throughout your 316 activity lifecycle:</p> 317 318 <pre> 319 public class MainActivity extends Activity { 320 private DrawerLayout mDrawerLayout; 321 private ActionBarDrawerToggle mDrawerToggle; 322 ... 323 324 public void onCreate(Bundle savedInstanceState) { 325 ... 326 327 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 328 mDrawerToggle = new ActionBarDrawerToggle( 329 this, /* host Activity */ 330 mDrawerLayout, /* DrawerLayout object */ 331 R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ 332 R.string.drawer_open, /* "open drawer" description */ 333 R.string.drawer_close /* "close drawer" description */ 334 ) { 335 336 /** Called when a drawer has settled in a completely closed state. */ 337 public void onDrawerClosed(View view) { 338 getActionBar().setTitle(mTitle); 339 } 340 341 /** Called when a drawer has settled in a completely open state. */ 342 public void onDrawerOpened(View drawerView) { 343 getActionBar().setTitle(mDrawerTitle); 344 } 345 }; 346 347 // Set the drawer toggle as the DrawerListener 348 mDrawerLayout.setDrawerListener(mDrawerToggle); 349 350 getActionBar().setDisplayHomeAsUpEnabled(true); 351 getActionBar().setHomeButtonEnabled(true); 352 } 353 354 @Override 355 protected void onPostCreate(Bundle savedInstanceState) { 356 super.onPostCreate(savedInstanceState); 357 // Sync the toggle state after onRestoreInstanceState has occurred. 358 mDrawerToggle.syncState(); 359 } 360 361 @Override 362 public void onConfigurationChanged(Configuration newConfig) { 363 super.onConfigurationChanged(newConfig); 364 mDrawerToggle.onConfigurationChanged(newConfig); 365 } 366 367 @Override 368 public boolean onOptionsItemSelected(MenuItem item) { 369 // Pass the event to ActionBarDrawerToggle, if it returns 370 // true, then it has handled the app icon touch event 371 if (mDrawerToggle.onOptionsItemSelected(item)) { 372 return true; 373 } 374 // Handle your other action bar items... 375 376 return super.onOptionsItemSelected(item); 377 } 378 379 ... 380 } 381 </pre> 382 383 <p>For a complete example of a navigation drawer, download the sample available at the 384 <a href="#top">top of the page</a>.</p> 385