画中画支持

Android 8.0(API 级别 26)允许以画中画 (PIP) 模式启动 Activity。画中画是一种特殊类型的多窗口模式,最常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。

画中画利用 Android 7.0 中的多窗口模式 API 来提供固定的视频叠加窗口。如要将画中画添加到您的应用中,您需要注册支持画中画的 Activity,根据需要将 Activity 切换为画中画模式,并确保当 Activity 处于画中画模式时,界面元素处于隐藏状态且视频能够继续播放。

画中画窗口会显示在屏幕的最上层,位于系统选择的一角。您可以将画中画窗口拖动到其他位置。当您点按该窗口时,会看到两个特殊的控件:全屏切换开关(位于窗口的中心)和关闭按钮(右上角的“X”)。

您的应用会控制当前 Activity 在何时进入画中画模式。下面是一些示例:

  • 一个 Activity 可以在用户点按主屏幕或最近使用的应用按钮来选择其他应用时,进入画中画模式。(Google 地图就是通过这种方式,在用户同时运行其他 Activity 时继续显示路线。)
  • 您的应用可以在用户从某个视频返回以浏览其他内容时,将该视频切换到画中画模式。
  • 您的应用可以在用户观看到某集内容结束时将视频切换到画中画模式。主屏幕会显示有关这部电视剧下一集的宣传信息或剧情摘要信息。
  • 您的应用可以提供一种方式,让用户可以在观看视频时将其他内容加入播放队列。当主屏幕显示内容选择 Activity 时,视频会继续以画中画模式播放。

声明对画中画的支持

默认情况下,系统不会自动为应用提供画中画支持。如果您想在应用中支持画中画,可以通过将 android:supportsPictureInPicture 设置为 true,在清单中注册视频 Activity。此外,指定您的 Activity 处理布局配置更改,这样一来,在画中画模式转换期间发生布局更改时,您的 Activity 就不会重新启动。

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

将您的 Activity 切换到画中画模式

如要进入画中画模式,Activity 必须调用 enterPictureInPictureMode()。例如,以下代码会在用户点击应用界面中的专用按钮时,将 Activity 切换到画中画模式:

Kotlin

override fun onActionClicked(action: Action) {
    if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
        activity?.enterPictureInPictureMode()
        return
    }
}

Java

@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

您可能需要添加将 Activity 切换到画中画模式(而不是进入后台)的逻辑。例如,如果用户在 Google 地图正在导航时按下主屏幕或最近使用的应用按钮,则该应用会切换到画中画模式。您可以通过替换 onUserLeaveHint() 来实现这一目的:

Kotlin

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

处理画中画模式下的界面元素

当 Activity 进入或退出画中画模式时,系统会调用 Activity.onPictureInPictureModeChanged()Fragment.onPictureInPictureModeChanged()

您应替换这些回调以重新绘制 Activity 的界面元素。请注意,在画中画模式下,您的 Activity 会在一个小窗口中显示。在画中画模式下,用户无法与界面元素互动,并且可能很难看清小界面元素的详细信息。界面极简的视频播放 Activity 可提供最佳的用户体验。Activity 应仅显示视频播放控件。在 Activity 进入画中画模式之前移除其他界面元素,并在 Activity 再次变为全屏时恢复这些元素:

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
                                           newConfig: Configuration) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
    }
}

Java

@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
        ...
    }
}

添加控件

画中画窗口可以在用户打开窗口菜单(通过点按移动设备上的窗口或使用电视遥控器选择菜单)时显示控件。

如果应用有处于活跃状态的媒体会话,则窗口会显示播放、暂停、下一个和上一个控件。

您还可以通过在进入画中画模式之前构建 PictureInPictureParams(使用 PictureInPictureParams.Builder.setActions())来明确指定自定义操作,并使用 enterPictureInPictureMode(android.app.PictureInPictureParams)setPictureInPictureParams(android.app.PictureInPictureParams) 在进入画中画模式时传递这些参数。请注意,如果您尝试添加的控件数量超过 getMaxNumPictureInPictureActions(),则系统只会添加上限数量的控件。

在画中画模式下继续播放视频

当您的 Activity 切换到画中画模式时,系统会将该 Activity 置于暂停状态并调用 Activity 的 onPause() 方法。当 Activity 在画中画模式下暂停时,视频播放不得暂停,而应继续播放。

在 Android 7.0 及更高版本中,当系统调用 Activity 的 onStop() 时,您应暂停视频播放;当系统调用 Activity 的 onStart() 时,您应恢复视频播放。这样一来,您就无需在 onPause() 中检查应用是否处于画中画模式,只需继续播放视频即可。

如果您必须在 onPause() 实现中暂停播放,请通过调用 isInPictureInPictureMode() 检查是否处于画中画模式并相应地处理播放情况,例如:

Kotlin

override fun onPause() {
    super.onPause()
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode) {
        // Continue playback
    } else {
        // Use existing playback logic for paused Activity behavior.
    }
}

Java

@Override
public void onPause() {
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    } else {
        // Use existing playback logic for paused Activity behavior.
        ...
    }
}

当您的 Activity 从画中画模式切换回全屏模式时,系统会恢复您的 Activity 并调用 onResume() 方法。

对单个播放 Activity 使用画中画模式

在您的应用中,可能会出现以下情况:有一个视频播放 Activity 正处于画中画模式,用户在主屏幕上浏览内容时选择了新的视频。应以全屏模式在现有的播放 Activity 中播放新的视频,而不是启动可能会令用户感到困惑的新 Activity。

如要确保将单个 Activity 用于视频播放请求并根据需要进入或退出画中画模式,请在清单中将 Activity 的 android:launchMode 设置为 singleTask

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

在您的 Activity 中,替换 onNewIntent() 并处理新的视频,从而根据需要停止任何现有的视频播放。

最佳做法

低内存设备可能无法使用画中画模式。在应用使用画中画之前,请务必通过调用 hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) 进行检查以确保可以使用画中画。

画中画旨在用于播放全屏视频的 Activity。将 Activity 切换到画中画模式时,请避免显示视频内容以外的任何内容。跟踪您的 Activity 何时进入画中画模式并隐藏界面元素,如处理画中画模式下的界面元素中所述。

由于画中画窗口在屏幕的一角显示为浮动窗口,因此您应避免在主屏幕中可能被画中画窗口遮盖的任何区域内显示重要信息。

当 Activity 进入画中画模式后,默认不会获得输入焦点。要在画中画模式下接收输入事件,请使用 MediaSession.setCallback()。如需详细了解如何使用 setCallback(),请参阅显示“正在播放”卡片

当您的应用处于画中画模式时,画中画窗口中的视频播放可能会对其他应用(例如,音乐播放器应用或语音搜索应用)造成音频干扰。为避免出现此问题,请在开始播放视频时请求音频焦点,并处理音频焦点更改通知,如管理音频焦点中所述。如果您在处于画中画模式时收到音频焦点丢失通知,请暂停或停止视频播放。

当您的应用即将进入画中画模式时,请注意,只有顶层 Activity 才会进入画中画模式。在某些情况下(例如在多窗口设备上),此时系统可能会显示下层 Activity,在画中画 Activity 旁边,您可能会再次看到下层 Activity。您应根据情况相应地处理这一问题,包括下层 Activity 获取 onResume()onPause() 回调。用户也有可能与该 Activity 进行交互。例如,如果您的视频列表 Activity 正在显示,视频播放 Activity 处于画中画模式,用户可能会从列表中选择新视频,画中画 Activity 应相应地进行更新。

更多示例代码

如需下载使用 Android 编写的示例应用,请参阅画中画示例。如需下载使用 Kotlin 编写的示例应用,请参阅 Android PictureInPicture 示例 (Kotlin)