Activity 画中画PiP

官网:https://developer.android.com/guide/topics/ui/picture-in-picture?hl=zh-cn
demo:
https://github.com/android/media-samples
https://github.com/android/media-samples/tree/main/PictureInPictureKotlin/

1.基本概念

从 Android 8.0(API 级别 26)开始,Android 允许以画中画 (PiP) 模式启动 activity

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

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

2.用户如何与画中画窗口互动

用户可以将画中画窗口拖动到其他位置。从 Android 12 开始,用户还可以执行以下操作:

(1)点按一次该窗口可显示全屏切换开关、关闭按钮、设置按钮以及应用提供的自定义操作(例如播放控件)。

(2)点按两次该窗口可在当前画中画大小与最大画中画大小之间切换。
将窗口拖到左侧或右侧边缘可隐藏该窗口;点按已隐藏窗口的可见部分或将其拖出可取消隐藏该窗口。

(3)使用“双指张合即可缩放”手势可调整画中画窗口的大小。

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

(1)一个 activity 可以在用户点按主屏幕按钮(在按钮导航模式下)或向上滑动到主屏幕(在手势导航模式下)时,进入画中画模式。(Google 地图就是通过这种方式,在用户同时运行其他 activity 时继续显示路线。)

(2)您的应用可以在用户从某个视频返回以浏览其他内容时,将该视频切换到画中画模式。

您的应用可以在用户观看到某集内容结束时,将视频切换到画中画模式。主屏幕会显示有关这部电视剧下一集的宣传信息或剧情摘要信息。

您的应用可以提供一种方式,让用户可以在观看视频时将其他内容加入播放队列。当主屏幕显示内容选择 activity 时,视频会继续以画中画模式播放。

3.声明对画中画的支持

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

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

4.将您的 activity 切换到画中画模式

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

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

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

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

4.1 使通过手势导航过渡到画中画模式变得更加顺畅

从 Android 12 开始,您可以使用 setAutoEnterEnabled 标志,在手势导航模式下向上滑动转到主屏幕时,更流畅地过渡到画中画模式。

如需实现此功能,请执行以下操作:

(1). 使用 setAutoEnterEnabled 构造 PictureInPictureParams.Builder,如下所示:

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setAspectRatio(aspectRatio)
    .setSourceRectHint(sourceRectHint)
    .setAutoEnterEnabled(true)
    .build());

注意:启用 setAutoEnterEnabled 后,无需在 onUserLeaveHint 中显式调用 enterPictureInPictureMode

(2). 尽早使用最新的 PictureInPictureParams 调用 setPictureInPictureParams。应用不应等待 onUserLeaveHint 回调(就像在 Android 11 中所做的那样)。

例如,应用可能要在第一次播放以及后续任何一次播放时(如果宽高比发生了变化)调用 setPictureInPictureParams。

(3).根据需要调用 setAutoEnterEnabled(false)。例如,如果当前播放处于暂停状态,则视频应用进入画中画模式可能不是最佳选择。

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

当 activity 进入或退出画中画模式时,
系统会调用 Activity.onPictureInPictureModeChanged() 或 Fragment.onPictureInPictureModeChanged()。
您应替换这些回调以重新绘制 activity 的界面元素。
请注意,在画中画模式下,您的 activity 会在一个小窗口中显示。
在画中画模式下,用户无法与应用的界面元素互动,并且可能很难看清小界面元素的详细信息。
界面极简的视频播放 activity 可提供最佳的用户体验。

如果您的应用需要为画中画提供自定义操作,请参阅本文档中的添加控件。
在 activity 进入画中画模式之前移除其他界面元素,并在 activity 再次变为全屏时恢复这些元素:

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.
    }
}

5.1 退出画中画模式时支持更流畅的动画

从 Android 12 开始,SourceRectHint 标志现在会重新用来在退出画中画模式时实现更流畅的动画。
退出时,系统会使用当前可用的 sourceRectHint 创建动画
无论它是用于进入画中画模式的原始 Rect,还是应用提供的更新后的 Rect。

如需实现此功能,请按以下方式更新您的应用:
(1)继续使用 sourceRectHint 和 aspectRatio 构造 PictureInPictureParams,以实现流畅的进入动画。
(2)如有必要,请在系统开始退出过渡之前更新 sourceRectHint。
当系统即将退出画中画模式时,activity 的视图层次结构会布局成它的目标配置(例如全屏)。
应用可以将布局更改监听器附加到其根视图或目标视图(如视频播放器视图),以检测事件并在动画开始前更新 sourceRectHint。

// Listener is called immediately after the user exits PiP but before animating.
playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                     oldLeft, oldTop, oldRight, oldBottom ->
    if (left != oldLeft || right != oldRight || top != oldTop
            || bottom != oldBottom) {
       // The playerView's bounds changed, update the source hint rect to
       // reflect its new bounds.
       val sourceRectHint = Rect()
       playerView.getGlobalVisibleRect(sourceRectHint)
       setPictureInPictureParams(
           PictureInPictureParams.Builder()
               .setSourceRectHint(sourceRectHint)
               .build()
       )
    }
}

5.2 添加控件

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

如果应用有处于活跃状态的媒体会话,则窗口会显示“播放”、“暂停”、“前进”和“后退”控件

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

5.3 为非视频内容停用无缝大小调整

Android 12 添加了 setSeamlessResizeEnabled 标志,在画中画窗口中调整非视频内容的大小时,该标志可提供更流畅的交替淡变动画。
以前,在画中画窗口中调整非视频内容的大小时会产生烦人的视觉伪影。

为了向后兼容,默认情况下,将 setSeamlessResizeEnabled 标志设置为 true。对于视频内容,请将其设置保留为 true;对于非视频内容,请将其更改为 false。

如需停用非视频内容的无缝大小调整,请编写以下代码:

setPictureInPictureParams(new PictureInPictureParams.Builder()
          .setSeamlessResizeEnabled(false)
          .build());

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

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

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

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

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.
    }
}

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

6.1 在画中画模式中使用单个播放 activity

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

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

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

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

6最佳做法

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

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

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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容