2013-03-26 18:53:01 -07:00
|
|
|
|
page.title=Creating a Navigation Drawer
|
2013-05-15 14:38:16 -07:00
|
|
|
|
page.tags="DrawerLayout", "navigation"
|
2013-03-26 18:53:01 -07:00
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
2013-05-16 10:41:15 -07:00
|
|
|
|
<div class="download-box">
|
2013-09-26 12:39:27 -07:00
|
|
|
|
<a href="http://developer.android.com/downloads/design/Android_Design_Icons_20130926.zip"
|
|
|
|
|
class="button">Download the Action Bar Icon Pack</a>
|
|
|
|
|
<p class="filename">Android_Design_Icons_20130926.zip</p>
|
2013-05-16 10:41:15 -07:00
|
|
|
|
</div>
|
|
|
|
|
|
2013-03-26 18:53:01 -07:00
|
|
|
|
</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
|
2013-07-23 19:35:17 -07:00
|
|
|
|
<a href="{@docRoot}tools/support-library/index.html">Support Library</a>.</p>
|
2013-03-26 18:53:01 -07:00
|
|
|
|
|
|
|
|
|
<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
|
2013-05-13 16:27:10 -07:00
|
|
|
|
<a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation Drawer</a> design guide.</p>
|
2013-03-26 18:53:01 -07:00
|
|
|
|
</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;
|
2013-07-18 10:18:57 -07:00
|
|
|
|
private DrawerLayout mDrawerLayout;
|
2013-03-26 18:53:01 -07:00
|
|
|
|
private ListView mDrawerList;
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
setContentView(R.layout.activity_main);
|
|
|
|
|
|
|
|
|
|
mPlanetTitles = getResources().getStringArray(R.array.planets_array);
|
2013-07-18 10:18:57 -07:00
|
|
|
|
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
2013-03-26 18:53:01 -07:00
|
|
|
|
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
|
2013-07-18 10:18:57 -07:00
|
|
|
|
mDrawerList.setItemChecked(position, true);
|
2013-03-26 18:53:01 -07:00
|
|
|
|
setTitle(mPlanetTitles[position]);
|
2013-07-18 10:18:57 -07:00
|
|
|
|
mDrawerLayout.closeDrawer(mDrawerList);
|
2013-03-26 18:53:01 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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>
|
|
|
|
|
|
2013-05-13 16:27:10 -07:00
|
|
|
|
<p>As discussed in the <a href="{@docRoot}design/patterns/navigation-drawer.html">Navigation
|
2013-03-26 18:53:01 -07:00
|
|
|
|
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.
|
2013-09-26 12:39:27 -07:00
|
|
|
|
<p>The standard navigation drawer icon is available in the <a href="http://developer.android.com/downloads/design/Android_Design_Icons_20130926.zip"
|
|
|
|
|
>Download the Action Bar Icon Pack</a>.</p>
|
2013-03-26 18:53:01 -07:00
|
|
|
|
<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);
|
2013-05-16 10:41:15 -07:00
|
|
|
|
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 */
|
|
|
|
|
) {
|
2013-03-26 18:53:01 -07:00
|
|
|
|
|
|
|
|
|
/** 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
|
2013-05-15 14:38:16 -07:00
|
|
|
|
<a href="#top">top of the page</a>.</p>
|