blob: e7366483e21bea6e4fa0d3b8087fe206adc3f435 [file] [log] [blame]
page.title=Providing Proper Back Navigation
page.tags=back navigation,NavUtils,TaskStackBuilder
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to:</h2>
<ol>
<li><a href="#SynthesizeBackStack">Synthesize a new Back Stack for Deep Links</a></li>
<li><a href="#back-fragments">Implement Back Navigation for Fragments</a></li>
<li><a href="#back-webviews">Implement Back Navigation for WebViews</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li>
<li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back Stack</a></li>
<li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li>
</ul>
</div>
</div>
<p><em>Back</em> navigation is how users move backward through the history of screens
they previously visited. All Android devices provide a <em>Back</em> button for this
type of navigation, so <strong>your app should not add a Back button to the UI</strong>.</p>
<p>In almost all situations, the system maintains a back stack of activities while the user
navigates your application. This allows the system to properly navigate backward when the user
presses the <em>Back</em> button. However, there are a few cases in which your app should manually
specify the <em>Back</em> behavior in order to provide the best user experience.</p>
<div class="note design">
<p><strong>Back Navigation Design</strong></p>
<p>Before continuing with this document, you should understand the
concepts and principles for <em>Back</em> navigation as described in
the <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design
guide.</p>
</div>
<p>Navigation patterns that require you to manually specify the <em>Back</em> behavior include:</p>
<ul>
<li>When the user enters a deep-level activity directly from a <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">notification</a>, an <a
href="{@docRoot}guide/topics/appwidgets/index.html">app widget</a>, or the <a
href="{@docRoot}training/implementing-navigation/nav-drawer.html">navigation drawer</a>.</li>
<li>Certain cases in which the user navigates between <a
href="{@docRoot}guide/components/fragments.html">fragments</a>.</li>
<li>When the user navigates web pages in a {@link android.webkit.WebView}.</li>
</ul>
<p>How to implement proper <em>Back</em> navigation in these situations is described
in the following sections.</p>
<h2 id="SynthesizeBackStack">Synthesize a new Back Stack for Deep Links</h2>
<p>Ordinarily, the system incrementally builds the back stack as the user navigates from one
activity to the next. However, when the user enters your app with a <em>deep link</em> that
starts the activity in its own task, it's necessary for you to synthesize a new
back stack because the activity is running in a new task without any back stack at all.</p>
<p>For example, when a notification takes the user to an activity deep in your app hierarchy,
you should add activities into your task's back stack so that pressing <em>Back</em> navigates
up the app hierarchy instead of exiting the app. This pattern is described further in the
<a href="{@docRoot}design/patterns/navigation.html#into-your-app"
>Navigation</a> design guide.</p>
<h3 id="SpecifyParent">Specify parent activities in the manifest</h3>
<p>Beginning in Android 4.1 (API level 16), you can declare the logical parent of each
activity by specifying the <a
href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code
android:parentActivityName}</a> attribute
in the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
&lt;activity>}</a> element. This allows the system to facilitate navigation patterns
because it can determine the logical <em>Back</em> or <em>Up</em> navigation path with this
information.</p>
<p>If your app supports Android 4.0 and lower, include the
<a href="{@docRoot}tools/support-library/index.html">Support Library</a> with your app and
add a <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code &lt;meta-data>}</a>
element inside the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
&lt;activity>}</a>. Then specify the parent activity as the value
for {@code android.support.PARENT_ACTIVITY}, matching the <a
href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code
android:parentActivityName}</a> attribute.</p>
<p>For example:</p>
<pre>
&lt;application ... >
...
&lt;!-- The main/home activity (it has no parent activity) -->
&lt;activity
android:name="com.example.myfirstapp.MainActivity" ...>
...
&lt;/activity>
&lt;!-- A child of the main activity -->
&lt;activity
android:name="com.example.myfirstapp.DisplayMessageActivity"
android:label="&#64;string/title_activity_display_message"
android:parentActivityName="com.example.myfirstapp.MainActivity" >
&lt;!-- The meta-data element is needed for versions lower than 4.1 -->
&lt;meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.MainActivity" />
&lt;/activity>
&lt;/application>
</pre>
<p>With the parent activity declared this way, you can use the
{@link android.support.v4.app.NavUtils} APIs to synthesize a new back stack by identifying which
activity is the appropriate parent for each activity.</p>
<h3 id="CreateBackStack">Create back stack when starting the activity</h3>
<p>Adding activities to the back stack begins upon the event that takes the user into your app.
That is, instead of calling {@link android.content.Context#startActivity startActivity()}, use the
{@link android.support.v4.app.TaskStackBuilder} APIs to define each activity that should
be placed into a new back stack. Then begin the target activity by calling {@link
android.support.v4.app.TaskStackBuilder#startActivities startActivities()}, or create the
appropriate {@link android.app.PendingIntent} by calling {@link
android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}.</p>
<p>For example, when a notification takes the user to an activity deep in your app hierarchy,
you can use this code to create a {@link android.app.PendingIntent}
that starts an activity and inserts a new back stack into the target task:</p>
<pre>
// Intent for the activity to open when user selects the notification
Intent detailsIntent = new Intent(this, DetailsActivity.class);
// Use TaskStackBuilder to build the back stack and get the PendingIntent
PendingIntent pendingIntent =
TaskStackBuilder.create(this)
// add all of DetailsActivity's parents to the stack,
// followed by DetailsActivity itself
.addNextIntentWithParentStack(upIntent)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(pendingIntent);
...
</pre>
<p>The resulting {@link android.app.PendingIntent} specifies not only the activity to
start (as defined by {@code detailsIntent}), but also the back stack that should be inserted
into the task (all parents of the {@code DetailsActivity} defined by {@code detailsIntent}).
So when the {@code DetailsActivity} starts, pressing <em>Back</em>
navigates backward through each of the {@code DetailsActivity} class's parent activities.</p>
<p class="note"><strong>Note:</strong> In order for the {@link
android.support.v4.app.TaskStackBuilder#addNextIntentWithParentStack addNextIntentWithParentStack()}
method to work,
you must declare the logical parent of each activity in your manifest file, using the
<a href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code
android:parentActivityName}</a> attribute (and corresponding <a
href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code &lt;meta-data>}</a> element)
as described above.</p>
<h2 id="back-fragments">Implement Back Navigation for Fragments</h2>
<p>When using fragments in your app, individual {@link android.app.FragmentTransaction}
objects may represent context changes that should be added to the back stack. For example, if you
are implementing a <a href="descendant.html#master-detail">master/detail flow</a> on a handset by
swapping out fragments, you should ensure that pressing the <em>Back</em> button on a detail
screen returns the user to the master screen. To do so, call {@link
android.app.FragmentTransaction#addToBackStack addToBackStack()} before you commit
the transaction:</p>
<pre>
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
</pre>
<p>When there are {@link android.app.FragmentTransaction} objects on the back stack and the user
presses the <em>Back</em> button,
the {@link android.app.FragmentManager} pops the most recent transaction off the back stack and
performs the reverse action (such as removing a fragment if the transaction added it).</p>
<p class="note"><strong>Note:</strong> You <strong>should not add transactions to the back
stack</strong> when the transaction is for horizontal navigation (such as when switching tabs)
or when modifying the content appearance (such as when adjusting filters). For more information,
about when <em>Back</em> navigation is appropriate,
see the <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design guide.</p>
<p>If your application updates other user interface elements to reflect the current state of your
fragments, such as the action bar, remember to update the UI when you commit the transaction. You
should update your user interface after the back stack changes in addition to
when you commit the transaction. You can listen for when a {@link android.app.FragmentTransaction}
is reverted by setting up an {@link android.app.FragmentManager.OnBackStackChangedListener}:</p>
<pre>
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
</pre>
<h2 id="back-webviews">Implement Back Navigation for WebViews</h2>
<p>If a part of your application is contained in a {@link android.webkit.WebView}, it may be
appropriate for <em>Back</em> to traverse browser history. To do so, you can override {@link
android.app.Activity#onBackPressed onBackPressed()} and proxy to the
{@link android.webkit.WebView} if it has history state:</p>
<pre>
{@literal @}Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
return;
}
// Otherwise defer to system default behavior.
super.onBackPressed();
}
</pre>
<p>Be careful when using this mechanism with highly dynamic web pages that can grow a large
history. Pages that generate an extensive history, such as those that make frequent changes to
the document hash, may make it tedious for users to get out of your activity.</p>
<p>For more information about using {@link android.webkit.WebView}, read <a
href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.