提供适当的“返回”导航

“返回”导航是用户在之前访问过的屏幕历史记录中向后移动的方式。所有 Android 设备都为此类导航提供了“返回”按钮,因此您的应用不应向界面添加“返回”按钮

在几乎所有情况下,当用户在应用中导航时,系统都会维护一个 Activity 返回栈。这样,当用户按“返回”按钮时,系统便可以正确地向后导航。不过,在少数情况下,应用应手动指定“返回”行为,以便提供最佳用户体验。

另请参阅设计返回和向上导航任务和返回栈以及 Android 设计:导航

要求您手动指定“返回”行为的导航模式包括:

下面几部分介绍了如何在这些情况下实现正确的“返回”导航。

为深层链接合成新的返回栈

通常,当用户从一个 Activity 导航到下一个 Activity 时,系统会以递增方式构建返回栈。不过,如果用户使用深层链接进入您的应用,而该链接在其自己的任务中启动 Activity,那么您需要合成新的返回栈,因为该 Activity 在新任务中运行而根本没有任何返回栈。

例如,当通知将用户带到应用层次结构深处的 Activity 时,您应将 Activity 添加到任务的返回栈中,这样一来,按“返回”按钮时,将沿应用层次结构向上导航而不是退出应用。导航设计指南中进一步介绍了此模式。

在清单中指定父 Activity

自 Android 4.1(API 级别 16)起,您可以通过在 <activity> 元素中指定 android:parentActivityName 属性,声明每个 Activity 的逻辑父项。这样,系统便可以为导航模式提供便利,因为它可以利用此信息确定逻辑“返回”或“向上”导航路径。

如果您的应用支持 Android 4.0 及更低版本,应使应用附带支持库,并在 <activity> 内添加 <meta-data> 元素。然后,将父 Activity 指定为 android.support.PARENT_ACTIVITY 的值,与 android:parentActivityName 属性匹配。

例如:

<application ... >
    ...
    <!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name="com.example.myfirstapp.MainActivity" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name="com.example.myfirstapp.DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName="com.example.myfirstapp.MainActivity" >
        <!-- The meta-data element is needed for versions lower than 4.1 -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>
</application>

以这种方式声明父 Activity 后,您可以通过识别每个 Activity 的相应父 Activity,使用 NavUtils API 合成新的返回栈。

在启动 Activity 时创建返回栈

将 Activity 添加到返回栈的过程始于将用户带入您应用的事件。也就是说,使用 TaskStackBuilder API 定义应放入新返回栈的每个 Activity,而不是调用 startActivity()。然后,通过调用 startActivities() 开始目标 Activity,或通过调用 getPendingIntent() 创建相应的 PendingIntent

例如,当通知将用户带到应用层次结构深处的 Activity 时,您可以使用以下代码创建一个 PendingIntent,用来启动 Activity 并将新的返回栈插入目标任务:

Kotlin

val detailsIntent = Intent(this, DetailsActivity::class.java)

val pendingIntent: PendingIntent? = TaskStackBuilder.create(this)
        // add all of DetailsActivity's parents to the stack,
        // followed by DetailsActivity itself
        .addNextIntentWithParentStack(detailsIntent)
        .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)

val builder = NotificationCompat.Builder(this)
        .setContentIntent(pendingIntent)
...

Java

// 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(detailsIntent)
                        .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(pendingIntent);
...

生成的 PendingIntent 不仅会指定要启动的 Activity(由 detailsIntent 定义),而且还指定应插入任务的返回栈(由 detailsIntent 定义的 DetailsActivity 的所有父项)。因此,DetailsActivity 启动后,如果按“返回”按钮,将向后导航经过 DetailsActivity 类的每个父 Activity。

注意:为使 addNextIntentWithParentStack() 方法起作用,您必须使用 android:parentActivityName 属性(以及相应的 <meta-data> 元素)在清单文件中声明每个 Activity 的逻辑父项,如上所述。

为 Fragment 实现“返回”导航

在应用中使用 Fragment 时,各个 FragmentTransaction 对象可能表示应添加到返回栈的上下文更改。例如,如果要通过置换出 Fragment 在手机上实现主/从流,您应确保在从屏幕上按“返回”按钮时可使用户返回到主屏幕。为此,请在提交事务之前调用 addToBackStack()

Kotlin

// Works with either the framework FragmentManager or the
// support package FragmentManager (supportFragmentManager).
supportFragmentManager.beginTransaction()
        .add(detailFragment, "detail")
        // Add this transaction to the back stack
        .addToBackStack(null)
        .commit()

Java

// 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(null)
                           .commit();

当返回栈中存在 FragmentTransaction 对象且用户按“返回”按钮时,FragmentManager 会从返回栈中弹出最近的事务并执行相反的操作(例如,如果事务添加了某个 Fragment,则将其移除)。

注意:如果事务用于水平导航(如切换标签)或修改内容外观(如调整过滤器),则不应将事务添加到返回栈。要详细了解“返回”导航何时合适,请参阅导航设计指南。

如果应用更新其他界面元素(如操作栏)以反映 Fragment 的当前状态,请记得在提交事务时更新界面。除了在提交事务时更新界面之外,您还应在返回栈更改后更新界面。您可以通过设置 FragmentManager.OnBackStackChangedListener 来监听 FragmentTransaction 何时还原了:

Kotlin

supportFragmentManager.addOnBackStackChangedListener {
    // Update your UI here.
}

Java

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });

为 WebView 实现“返回”导航

如果应用的一部分包含在 WebView 中,则按“返回”按钮来遍历浏览器历史记录可能是一种合适的做法。为此,您可以替换 onBackPressed() 并使用 WebView(如果它具有历史记录状态):

Kotlin

override fun onBackPressed() {
    if (mWebView.canGoBack()) {
        mWebView.goBack()
    } else {
        // Otherwise defer to system default behavior.
        super.onBackPressed()
    }
}

Java

@Override
public void onBackPressed() {
    if (mWebView.canGoBack()) {
        mWebView.goBack();
        return;
    }

    // Otherwise defer to system default behavior.
    super.onBackPressed();
}

对高度动态的网页应用此机制时应格外小心,这些网页可能会产生庞大的历史记录。生成大量历史记录的页面(如频繁更改文档哈希值的页面)可能会变得冗长乏味,从而导致用户离开您的 Activity。

要详细了解如何使用 WebView,请参阅在 WebView 中开发网络应用