Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 1 | page.title=Providing Proper Back Navigation |
Joe Fernandez | 33baa5a | 2013-11-14 11:41:19 -0800 | [diff] [blame] | 2 | page.tags=back navigation,NavUtils,TaskStackBuilder |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 3 | |
| 4 | trainingnavtop=true |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 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> |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 13 | <li><a href="#SynthesizeBackStack">Synthesize a new Back Stack for Deep Links</a></li> |
| 14 | <li><a href="#back-fragments">Implement Back Navigation for Fragments</a></li> |
| 15 | <li><a href="#back-webviews">Implement Back Navigation for WebViews</a></li> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 16 | </ol> |
| 17 | |
| 18 | <h2>You should also read</h2> |
| 19 | <ul> |
| 20 | <li><a href="{@docRoot}training/design-navigation/ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></li> |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 21 | <li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back Stack</a></li> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 22 | <li><a href="{@docRoot}design/patterns/navigation.html">Android Design: Navigation</a></li> |
| 23 | </ul> |
| 24 | |
| 25 | </div> |
| 26 | </div> |
| 27 | |
| 28 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 29 | <p><em>Back</em> navigation is how users move backward through the history of screens |
| 30 | they previously visited. All Android devices provide a <em>Back</em> button for this |
| 31 | type of navigation, so <strong>your app should not add a Back button to the UI</strong>.</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 32 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 33 | <p>In almost all situations, the system maintains a back stack of activities while the user |
| 34 | navigates your application. This allows the system to properly navigate backward when the user |
| 35 | presses the <em>Back</em> button. However, there are a few cases in which your app should manually |
| 36 | specify the <em>Back</em> behavior in order to provide the best user experience.</p> |
| 37 | |
| 38 | <div class="note design"> |
| 39 | <p><strong>Back Navigation Design</strong></p> |
| 40 | <p>Before continuing with this document, you should understand the |
| 41 | concepts and principles for <em>Back</em> navigation as described in |
| 42 | the <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design |
| 43 | guide.</p> |
| 44 | </div> |
| 45 | |
| 46 | <p>Navigation patterns that require you to manually specify the <em>Back</em> behavior include:</p> |
| 47 | <ul> |
| 48 | <li>When the user enters a deep-level activity directly from a <a |
| 49 | href="{@docRoot}guide/topics/ui/notifiers/notifications.html">notification</a>, an <a |
| 50 | href="{@docRoot}guide/topics/appwidgets/index.html">app widget</a>, or the <a |
| 51 | href="{@docRoot}training/implementing-navigation/nav-drawer.html">navigation drawer</a>.</li> |
| 52 | <li>Certain cases in which the user navigates between <a |
| 53 | href="{@docRoot}guide/components/fragments.html">fragments</a>.</li> |
| 54 | <li>When the user navigates web pages in a {@link android.webkit.WebView}.</li> |
| 55 | </ul> |
| 56 | |
| 57 | <p>How to implement proper <em>Back</em> navigation in these situations is described |
| 58 | in the following sections.</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 59 | |
| 60 | |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 61 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 62 | <h2 id="SynthesizeBackStack">Synthesize a new Back Stack for Deep Links</h2> |
| 63 | |
| 64 | <p>Ordinarily, the system incrementally builds the back stack as the user navigates from one |
| 65 | activity to the next. However, when the user enters your app with a <em>deep link</em> that |
| 66 | starts the activity in its own task, it's necessary for you to synthesize a new |
| 67 | back stack because the activity is running in a new task without any back stack at all.</p> |
| 68 | |
| 69 | <p>For example, when a notification takes the user to an activity deep in your app hierarchy, |
| 70 | you should add activities into your task's back stack so that pressing <em>Back</em> navigates |
| 71 | up the app hierarchy instead of exiting the app. This pattern is described further in the |
| 72 | <a href="{@docRoot}design/patterns/navigation.html#into-your-app" |
| 73 | >Navigation</a> design guide.</p> |
| 74 | |
| 75 | |
| 76 | <h3 id="SpecifyParent">Specify parent activities in the manifest</h3> |
| 77 | |
| 78 | <p>Beginning in Android 4.1 (API level 16), you can declare the logical parent of each |
| 79 | activity by specifying the <a |
| 80 | href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code |
| 81 | android:parentActivityName}</a> attribute |
| 82 | in the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code |
| 83 | <activity>}</a> element. This allows the system to facilitate navigation patterns |
| 84 | because it can determine the logical <em>Back</em> or <em>Up</em> navigation path with this |
| 85 | information.</p> |
| 86 | |
| 87 | <p>If your app supports Android 4.0 and lower, include the |
Scott Main | 4e2c9dc | 2013-07-23 19:35:17 -0700 | [diff] [blame] | 88 | <a href="{@docRoot}tools/support-library/index.html">Support Library</a> with your app and |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 89 | add a <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code <meta-data>}</a> |
| 90 | element inside the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code |
| 91 | <activity>}</a>. Then specify the parent activity as the value |
| 92 | for {@code android.support.PARENT_ACTIVITY}, matching the <a |
| 93 | href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code |
| 94 | android:parentActivityName}</a> attribute.</p> |
| 95 | |
| 96 | <p>For example:</p> |
| 97 | |
| 98 | <pre> |
| 99 | <application ... > |
| 100 | ... |
| 101 | <!-- The main/home activity (it has no parent activity) --> |
| 102 | <activity |
| 103 | android:name="com.example.myfirstapp.MainActivity" ...> |
| 104 | ... |
| 105 | </activity> |
| 106 | <!-- A child of the main activity --> |
| 107 | <activity |
| 108 | android:name="com.example.myfirstapp.DisplayMessageActivity" |
| 109 | android:label="@string/title_activity_display_message" |
| 110 | android:parentActivityName="com.example.myfirstapp.MainActivity" > |
| 111 | <!-- The meta-data element is needed for versions lower than 4.1 --> |
| 112 | <meta-data |
| 113 | android:name="android.support.PARENT_ACTIVITY" |
| 114 | android:value="com.example.myfirstapp.MainActivity" /> |
| 115 | </activity> |
| 116 | </application> |
| 117 | </pre> |
| 118 | |
| 119 | <p>With the parent activity declared this way, you can use the |
| 120 | {@link android.support.v4.app.NavUtils} APIs to synthesize a new back stack by identifying which |
| 121 | activity is the appropriate parent for each activity.</p> |
| 122 | |
| 123 | |
| 124 | |
| 125 | <h3 id="CreateBackStack">Create back stack when starting the activity</h3> |
| 126 | |
| 127 | <p>Adding activities to the back stack begins upon the event that takes the user into your app. |
| 128 | That is, instead of calling {@link android.content.Context#startActivity startActivity()}, use the |
| 129 | {@link android.support.v4.app.TaskStackBuilder} APIs to define each activity that should |
| 130 | be placed into a new back stack. Then begin the target activity by calling {@link |
| 131 | android.support.v4.app.TaskStackBuilder#startActivities startActivities()}, or create the |
| 132 | appropriate {@link android.app.PendingIntent} by calling {@link |
| 133 | android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}.</p> |
| 134 | |
| 135 | <p>For example, when a notification takes the user to an activity deep in your app hierarchy, |
| 136 | you can use this code to create a {@link android.app.PendingIntent} |
| 137 | that starts an activity and inserts a new back stack into the target task:</p> |
| 138 | |
| 139 | <pre> |
| 140 | // Intent for the activity to open when user selects the notification |
| 141 | Intent detailsIntent = new Intent(this, DetailsActivity.class); |
| 142 | |
| 143 | // Use TaskStackBuilder to build the back stack and get the PendingIntent |
| 144 | PendingIntent pendingIntent = |
| 145 | TaskStackBuilder.create(this) |
| 146 | // add all of DetailsActivity's parents to the stack, |
| 147 | // followed by DetailsActivity itself |
| 148 | .addNextIntentWithParentStack(upIntent) |
| 149 | .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); |
| 150 | |
| 151 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this); |
| 152 | builder.setContentIntent(pendingIntent); |
| 153 | ... |
| 154 | </pre> |
| 155 | |
| 156 | <p>The resulting {@link android.app.PendingIntent} specifies not only the activity to |
| 157 | start (as defined by {@code detailsIntent}), but also the back stack that should be inserted |
| 158 | into the task (all parents of the {@code DetailsActivity} defined by {@code detailsIntent}). |
| 159 | So when the {@code DetailsActivity} starts, pressing <em>Back</em> |
| 160 | navigates backward through each of the {@code DetailsActivity} class's parent activities.</p> |
| 161 | |
| 162 | <p class="note"><strong>Note:</strong> In order for the {@link |
| 163 | android.support.v4.app.TaskStackBuilder#addNextIntentWithParentStack addNextIntentWithParentStack()} |
| 164 | method to work, |
| 165 | you must declare the logical parent of each activity in your manifest file, using the |
| 166 | <a href="{@docRoot}guide/topics/manifest/activity-element.html#parent">{@code |
| 167 | android:parentActivityName}</a> attribute (and corresponding <a |
| 168 | href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code <meta-data>}</a> element) |
| 169 | as described above.</p> |
| 170 | |
| 171 | |
| 172 | |
| 173 | |
| 174 | |
| 175 | <h2 id="back-fragments">Implement Back Navigation for Fragments</h2> |
| 176 | |
| 177 | <p>When using fragments in your app, individual {@link android.app.FragmentTransaction} |
| 178 | objects may represent context changes that should be added to the back stack. For example, if you |
| 179 | are implementing a <a href="descendant.html#master-detail">master/detail flow</a> on a handset by |
| 180 | swapping out fragments, you should ensure that pressing the <em>Back</em> button on a detail |
| 181 | screen returns the user to the master screen. To do so, call {@link |
| 182 | android.app.FragmentTransaction#addToBackStack addToBackStack()} before you commit |
| 183 | the transaction:</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 184 | |
| 185 | <pre> |
| 186 | // Works with either the framework FragmentManager or the |
| 187 | // support package FragmentManager (getSupportFragmentManager). |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 188 | getSupportFragmentManager().beginTransaction() |
| 189 | .add(detailFragment, "detail") |
| 190 | // Add this transaction to the back stack |
| 191 | .addToBackStack() |
| 192 | .commit(); |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 193 | </pre> |
| 194 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 195 | <p>When there are {@link android.app.FragmentTransaction} objects on the back stack and the user |
| 196 | presses the <em>Back</em> button, |
| 197 | the {@link android.app.FragmentManager} pops the most recent transaction off the back stack and |
| 198 | performs the reverse action (such as removing a fragment if the transaction added it).</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 199 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 200 | <p class="note"><strong>Note:</strong> You <strong>should not add transactions to the back |
| 201 | stack</strong> when the transaction is for horizontal navigation (such as when switching tabs) |
| 202 | or when modifying the content appearance (such as when adjusting filters). For more information, |
| 203 | about when <em>Back</em> navigation is appropriate, |
| 204 | see the <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design guide.</p> |
| 205 | |
| 206 | <p>If your application updates other user interface elements to reflect the current state of your |
| 207 | fragments, such as the action bar, remember to update the UI when you commit the transaction. You |
| 208 | should update your user interface after the back stack changes in addition to |
| 209 | when you commit the transaction. You can listen for when a {@link android.app.FragmentTransaction} |
| 210 | is reverted by setting up an {@link android.app.FragmentManager.OnBackStackChangedListener}:</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 211 | |
| 212 | <pre> |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 213 | getSupportFragmentManager().addOnBackStackChangedListener( |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 214 | new FragmentManager.OnBackStackChangedListener() { |
| 215 | public void onBackStackChanged() { |
| 216 | // Update your UI here. |
| 217 | } |
| 218 | }); |
| 219 | </pre> |
| 220 | |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 221 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 222 | |
| 223 | <h2 id="back-webviews">Implement Back Navigation for WebViews</h2> |
| 224 | |
| 225 | <p>If a part of your application is contained in a {@link android.webkit.WebView}, it may be |
| 226 | appropriate for <em>Back</em> to traverse browser history. To do so, you can override {@link |
| 227 | android.app.Activity#onBackPressed onBackPressed()} and proxy to the |
| 228 | {@link android.webkit.WebView} if it has history state:</p> |
Roman Nurik | 19266f7 | 2012-03-12 21:48:47 -0700 | [diff] [blame] | 229 | |
| 230 | <pre> |
| 231 | {@literal @}Override |
| 232 | public void onBackPressed() { |
| 233 | if (mWebView.canGoBack()) { |
| 234 | mWebView.goBack(); |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | // Otherwise defer to system default behavior. |
| 239 | super.onBackPressed(); |
| 240 | } |
| 241 | </pre> |
| 242 | |
Scott Main | 7c84055 | 2013-03-26 18:53:01 -0700 | [diff] [blame] | 243 | <p>Be careful when using this mechanism with highly dynamic web pages that can grow a large |
| 244 | history. Pages that generate an extensive history, such as those that make frequent changes to |
| 245 | the document hash, may make it tedious for users to get out of your activity.</p> |
| 246 | |
| 247 | <p>For more information about using {@link android.webkit.WebView}, read <a |
| 248 | href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>. |