Home | History | Annotate | Download | only in implementing-navigation
      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 &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
    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     &#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);
    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&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
    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     &#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 */
    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 &#64;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     &#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) {
    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     &#64;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     &#64;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     &#64;Override
    362     public void onConfigurationChanged(Configuration newConfig) {
    363         super.onConfigurationChanged(newConfig);
    364         mDrawerToggle.onConfigurationChanged(newConfig);
    365     }
    366 
    367     &#64;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