| page.title=Creating a Navigation Drawer |
| page.tags="DrawerLayout", "navigation" |
| |
| trainingnavtop=true |
| |
| @jd:body |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>This lesson teaches you to:</h2> |
| <ol> |
| <li><a href="#DrawerLayout">Create a Drawer Layout</a></li> |
| <li><a href="#Init">Initialize the Drawer List</a></li> |
| <li><a href="#ListItemClicks">Handle Navigation Click Events</a></li> |
| <li><a href="#OpenClose">Listen for Open and Close Events</a></li> |
| <li><a href="#ActionBarIcon">Open and Close with the App Icon</a></li> |
| </ol> |
| |
| <h2>Try it out</h2> |
| |
| <div class="download-box"> |
| <a href="http://developer.android.com/shareables/training/NavigationDrawer.zip" |
| class="button">Download the sample app</a> |
| <p class="filename">NavigationDrawer.zip</p> |
| </div> |
| |
| <div class="download-box"> |
| <a href="http://developer.android.com/downloads/design/Android_Navigation_Drawer_Icon_20130516.zip" |
| class="button">Download the nav drawer icons</a> |
| <p class="filename">Android_Navigation_Drawer_Icon_20130516.zip</p> |
| </div> |
| |
| </div> |
| </div> |
| |
| |
| |
| <p>The navigation drawer is a panel that displays the app’s main navigation options |
| on the left edge of the screen. It is hidden most of the time, but is revealed |
| when the user swipes a finger from the left edge of the screen or, while at the top level of the |
| app, the user touches the app icon in the action bar.</p> |
| |
| <p>This lesson describes how to implement a navigation drawer using the |
| {@link android.support.v4.widget.DrawerLayout} APIs available in the |
| <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>.</p> |
| |
| <div class="note design"> |
| <p><strong>Navigation Drawer Design</strong></p> |
| <p>Before you decide to use a navigation drawer in your app, you should understand the use |
| cases and design principles defined in the |
| <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation Drawer</a> design guide.</p> |
| </div> |
| |
| |
| <h2 id="DrawerLayout">Create a Drawer Layout</h2> |
| |
| <p>To add a navigation drawer, declare your user interface with a |
| {@link android.support.v4.widget.DrawerLayout} object as the root view of your layout. |
| Inside the {@link android.support.v4.widget.DrawerLayout}, add one view that contains |
| the main content for the screen (your primary layout when the drawer is hidden) and another view |
| that contains the contents of the navigation drawer.</p> |
| |
| <p>For example, the following layout uses a {@link |
| android.support.v4.widget.DrawerLayout} with two child views: a {@link android.widget.FrameLayout} |
| to contain the main content (populated by a {@link android.app.Fragment} at |
| runtime), and a {@link android.widget.ListView} for the navigation drawer.</p> |
| |
| <pre> |
| <android.support.v4.widget.DrawerLayout |
| xmlns:android="http://schemas.android.com/apk/res/android" |
| android:id="@+id/drawer_layout" |
| android:layout_width="match_parent" |
| android:layout_height="match_parent"> |
| <!-- The main content view --> |
| <FrameLayout |
| android:id="@+id/content_frame" |
| android:layout_width="match_parent" |
| android:layout_height="match_parent" /> |
| <!-- The navigation drawer --> |
| <ListView android:id="@+id/left_drawer" |
| android:layout_width="240dp" |
| android:layout_height="match_parent" |
| android:layout_gravity="start" |
| android:choiceMode="singleChoice" |
| android:divider="@android:color/transparent" |
| android:dividerHeight="0dp" |
| android:background="#111"/> |
| </android.support.v4.widget.DrawerLayout> |
| </pre> |
| |
| <p>This layout demonstrates some important layout characteristics:</p> |
| <ul> |
| <li>The main content view (the {@link android.widget.FrameLayout} above) |
| <strong>must be the first child</strong> in the {@link |
| android.support.v4.widget.DrawerLayout} because the XML order implies z-ordering |
| and the drawer must be on top of the content.</li> |
| <li>The main content view is set to match the parent |
| view's width and height, because it represents the entire UI when the |
| navigation drawer is hidden.</li> |
| <li>The drawer view (the {@link android.widget.ListView}) <strong>must specify its horizontal |
| gravity</strong> with the {@code android:layout_gravity} attribute. To |
| support right-to-left (RTL) languages, specify the value with {@code "start"} |
| instead of {@code "left"} (so the drawer appears on the right when the layout is RTL).</p> |
| </li> |
| <li>The drawer view specifies its width in {@code dp} units and the height matches the parent |
| view. The drawer width should be no more than 320dp so the user can always |
| see a portion of the main content.</li> |
| </ul> |
| |
| |
| |
| <h2 id="Init">Initialize the Drawer List</h2> |
| |
| <p>In your activity, one of the first things to do is initialize |
| the navigation drawer's list of items. How you do so depends on the content of your app, but |
| a navigation drawer often consists of a {@link android.widget.ListView}, so the list |
| should be populated by an {@link android.widget.Adapter} (such as {@link |
| android.widget.ArrayAdapter} or {@link android.widget.SimpleCursorAdapter}).</p> |
| |
| <p>For example, here's how you can initialize the navigation list with a |
| <a href="{@docRoot}guide/topics/resources/string-resource.html#StringArray">string array</a>:</p> |
| |
| <pre> |
| public class MainActivity extends Activity { |
| private String[] mPlanetTitles; |
| private DrawerLayout mDrawerLayout; |
| private ListView mDrawerList; |
| ... |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| |
| mPlanetTitles = getResources().getStringArray(R.array.planets_array); |
| mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); |
| mDrawerList = (ListView) findViewById(R.id.left_drawer); |
| |
| // Set the adapter for the list view |
| mDrawerList.setAdapter(new ArrayAdapter<String>(this, |
| R.layout.drawer_list_item, mPlanetTitles)); |
| // Set the list's click listener |
| mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); |
| |
| ... |
| } |
| } |
| </pre> |
| |
| <p>This code also calls {@link android.widget.ListView#setOnItemClickListener |
| setOnItemClickListener()} to receive click events in the navigation drawer's list. |
| The next section shows how to implement this interface |
| and change the content view when the user selects an item.</p> |
| |
| |
| |
| <h2 id="ListItemClicks">Handle Navigation Click Events</h2> |
| |
| <p>When the user selects an item in the drawer's list, the system calls {@link |
| android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()} on the |
| {@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} given to |
| {@link android.widget.ListView#setOnItemClickListener setOnItemClickListener()}.</p> |
| |
| <p>What you do in the {@link |
| android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()} method |
| depends on how you've implemented your <a |
| href="{@docRoot}design/patterns/app-structure.html">app structure</a>. In the following example, |
| selecting each item in the list inserts a different {@link |
| android.app.Fragment} into the main content view (the |
| {@link android.widget.FrameLayout} element identified by the {@code R.id.content_frame} ID):</p> |
| |
| <pre> |
| private class DrawerItemClickListener implements ListView.OnItemClickListener { |
| @Override |
| public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
| selectItem(position); |
| } |
| } |
| |
| /** Swaps fragments in the main content view */ |
| private void selectItem(int position) { |
| // Create a new fragment and specify the planet to show based on position |
| Fragment fragment = new PlanetFragment(); |
| Bundle args = new Bundle(); |
| args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); |
| fragment.setArguments(args); |
| |
| // Insert the fragment by replacing any existing fragment |
| FragmentManager fragmentManager = getFragmentManager(); |
| fragmentManager.beginTransaction() |
| .replace(R.id.content_frame, fragment) |
| .commit(); |
| |
| // Highlight the selected item, update the title, and close the drawer |
| mDrawerList.setItemChecked(position, true); |
| setTitle(mPlanetTitles[position]); |
| mDrawerLayout.closeDrawer(mDrawerList); |
| } |
| |
| @Override |
| public void setTitle(CharSequence title) { |
| mTitle = title; |
| getActionBar().setTitle(mTitle); |
| } |
| |
| </pre> |
| |
| |
| |
| |
| <h2 id="OpenClose">Listen for Open and Close Events</h2> |
| |
| <p>To listen for drawer open and close events, call {@link |
| android.support.v4.widget.DrawerLayout#setDrawerListener setDrawerListener()} on your |
| {@link android.support.v4.widget.DrawerLayout} and pass it an implementation of |
| {@link android.support.v4.widget.DrawerLayout.DrawerListener}. This interface provides callbacks |
| for drawer events such as {@link |
| android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerOpened onDrawerOpened()} and {@link |
| android.support.v4.widget.DrawerLayout.DrawerListener#onDrawerClosed onDrawerClosed()}.</p> |
| |
| <p>However, rather than implementing the {@link |
| android.support.v4.widget.DrawerLayout.DrawerListener}, if your activity includes the |
| <a href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, you can instead |
| extend the {@link android.support.v4.app.ActionBarDrawerToggle} class. The |
| {@link android.support.v4.app.ActionBarDrawerToggle} implements |
| {@link android.support.v4.widget.DrawerLayout.DrawerListener} so you can still override those |
| callbacks, but it also facilitates the proper |
| interaction behavior between the action bar icon and the navigation drawer (discussed further in |
| the next section).</p> |
| |
| <p>As discussed in the <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation |
| Drawer</a> design guide, you should modify the contents of the action bar |
| when the drawer is visible, such as to change the title and remove action items that are |
| contextual to the main content. The following code shows how you can do so by overriding {@link |
| android.support.v4.widget.DrawerLayout.DrawerListener} callback methods with an instance |
| of the {@link android.support.v4.app.ActionBarDrawerToggle} class:</p> |
| |
| <pre> |
| public class MainActivity extends Activity { |
| private DrawerLayout mDrawerLayout; |
| private ActionBarDrawerToggle mDrawerToggle; |
| private CharSequence mDrawerTitle; |
| private CharSequence mTitle; |
| ... |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| ... |
| |
| mTitle = mDrawerTitle = getTitle(); |
| mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); |
| mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, |
| R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) { |
| |
| /** Called when a drawer has settled in a completely closed state. */ |
| public void onDrawerClosed(View view) { |
| getActionBar().setTitle(mTitle); |
| invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() |
| } |
| |
| /** Called when a drawer has settled in a completely open state. */ |
| public void onDrawerOpened(View drawerView) { |
| getActionBar().setTitle(mDrawerTitle); |
| invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() |
| } |
| }; |
| |
| // Set the drawer toggle as the DrawerListener |
| mDrawerLayout.setDrawerListener(mDrawerToggle); |
| } |
| |
| /* Called whenever we call invalidateOptionsMenu() */ |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| // If the nav drawer is open, hide action items related to the content view |
| boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); |
| menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); |
| return super.onPrepareOptionsMenu(menu); |
| } |
| } |
| </pre> |
| |
| <p>The next section describes the {@link android.support.v4.app.ActionBarDrawerToggle} constructor |
| arguments and the other steps required to set it up to handle interaction with the |
| action bar icon.</p> |
| |
| |
| |
| <h2 id="ActionBarIcon">Open and Close with the App Icon</h2> |
| |
| <p>Users can open and close the navigation drawer with a swipe gesture from or towards the left |
| edge of the screen, but if you're using the <a |
| href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, you should also allow users to |
| open and close it by touching the app icon. And the app icon should also indicate the presence of |
| the navigation drawer with a special icon. You can implement all this behavior by using the |
| {@link android.support.v4.app.ActionBarDrawerToggle} shown in the previous section.</p> |
| |
| <p>To make {@link android.support.v4.app.ActionBarDrawerToggle} work, create an instance of |
| it with its constructor, which requires the following arguments:</p> |
| <ul> |
| <li>The {@link android.app.Activity} hosting the drawer. |
| <li>The {@link android.support.v4.widget.DrawerLayout}. |
| <li>A drawable resource to use as the drawer indicator. |
| <p><a href="http://developer.android.com/downloads/design/Android_Navigation_Drawer_Icon_20130516.zip" |
| >Download the standard navigation icons</a> (available for both dark and light themes).</p> |
| <li>A String resource to describe the "open drawer" action (for accessibility). |
| <li>A String resource to describe the "close drawer" action (for accessibility). |
| </ul> |
| |
| <p>Then, whether or not you've created a subclass of |
| {@link android.support.v4.app.ActionBarDrawerToggle} as your drawer listener, you need to call |
| upon your {@link android.support.v4.app.ActionBarDrawerToggle} in a few places throughout your |
| activity lifecycle:</p> |
| |
| <pre> |
| public class MainActivity extends Activity { |
| private DrawerLayout mDrawerLayout; |
| private ActionBarDrawerToggle mDrawerToggle; |
| ... |
| |
| public void onCreate(Bundle savedInstanceState) { |
| ... |
| |
| mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); |
| mDrawerToggle = new ActionBarDrawerToggle( |
| this, /* host Activity */ |
| mDrawerLayout, /* DrawerLayout object */ |
| R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ |
| R.string.drawer_open, /* "open drawer" description */ |
| R.string.drawer_close /* "close drawer" description */ |
| ) { |
| |
| /** Called when a drawer has settled in a completely closed state. */ |
| public void onDrawerClosed(View view) { |
| getActionBar().setTitle(mTitle); |
| } |
| |
| /** Called when a drawer has settled in a completely open state. */ |
| public void onDrawerOpened(View drawerView) { |
| getActionBar().setTitle(mDrawerTitle); |
| } |
| }; |
| |
| // Set the drawer toggle as the DrawerListener |
| mDrawerLayout.setDrawerListener(mDrawerToggle); |
| |
| getActionBar().setDisplayHomeAsUpEnabled(true); |
| getActionBar().setHomeButtonEnabled(true); |
| } |
| |
| @Override |
| protected void onPostCreate(Bundle savedInstanceState) { |
| super.onPostCreate(savedInstanceState); |
| // Sync the toggle state after onRestoreInstanceState has occurred. |
| mDrawerToggle.syncState(); |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| super.onConfigurationChanged(newConfig); |
| mDrawerToggle.onConfigurationChanged(newConfig); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| // Pass the event to ActionBarDrawerToggle, if it returns |
| // true, then it has handled the app icon touch event |
| if (mDrawerToggle.onOptionsItemSelected(item)) { |
| return true; |
| } |
| // Handle your other action bar items... |
| |
| return super.onOptionsItemSelected(item); |
| } |
| |
| ... |
| } |
| </pre> |
| |
| <p>For a complete example of a navigation drawer, download the sample available at the |
| <a href="#top">top of the page</a>.</p> |