设置您的应用以使用画中画功能

在 AndroidManifest.xml 文件的 activity 标记中,执行以下操作:

  1. 添加 supportsPictureInPicture 并将其设置为 true,以声明您将在应用中使用画中画 (PiP)。
  2. 添加 configChanges 并将其设置为 orientation|screenLayout|screenSize|smallestScreenSize,以指定您的 activity 处理布局配置更改。这样一来,如果在画中画模式转换期间出现布局更改,您的 activity 就不会重新启动。
<activity
    android:name=".SnippetsActivity"
    android:exported="true"
    android:supportsPictureInPicture="true"
    android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
    android:theme="@style/Theme.Snippets">

在您的 Compose 代码中,执行以下操作:

  1. 在 Context 上添加此扩展程序。在本指南中,您将多次使用此扩展程序来访问 activity。
    internal fun Context.findActivity(): ComponentActivity {
        var context = this
        while (context is ContextWrapper) {
            if (context is ComponentActivity) return context
            context = context.baseContext
        }
        throw IllegalStateException("Picture in picture should be called in the context of an Activity")
    }

为 Android 12 之前的版本添加了离开应用时的 PiP

如需为 Android 12 之前的版本添加 PiP,请使用 addOnUserLeaveHintProvider。如需为 Android 12 之前的版本添加画中画功能,请按以下步骤操作:

  1. 添加版本门,以便仅在版本 O 到 R 中访问此代码。
  2. 使用 DisposableEffect,并将 Context 作为键。
  3. 在 DisposableEffect 内,使用 lambda 定义触发 onUserLeaveHintProvider 时的行为。在 lambda 中,对 findActivity() 调用 enterPictureInPictureMode(),并传入 PictureInPictureParams.Builder().build()。
  4. 使用 findActivity() 添加 addOnUserLeaveHintListener 并传入 lambda。
  5. 在 onDispose 中,使用 findActivity() 添加 removeOnUserLeaveHintListener 并传入 lambda。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
    Build.VERSION.SDK_INT < Build.VERSION_CODES.S
) {
    val context = LocalContext.current
    DisposableEffect(context) {
        val onUserLeaveBehavior = Runnable {
            context.findActivity()
                .enterPictureInPictureMode(PictureInPictureParams.Builder().build())
        }
        context.findActivity().addOnUserLeaveHintListener(
            onUserLeaveBehavior
        )
        onDispose {
            context.findActivity().removeOnUserLeaveHintListener(
                onUserLeaveBehavior
            )
        }
    }
} else {
    Log.i("PiP info", "API does not support PiP")
}

为 Android 12 之后的版本添加了在离开应用时启用 PiP 的功能

在 Android 12 之后,通过传递给应用视频播放器的修饰符添加 PictureInPictureParams.Builder。

  1. 创建一个 modifier,并对其调用 onGloballyPositioned。布局坐标将在后面的步骤中使用。
  2. 为 PictureInPictureParams.Builder() 创建一个变量。
  3. 添加 if 语句以检查 SDK 是否为 S 或更高版本。如果是,请向构建器添加 setAutoEnterEnabled 并将其设置为 true,以便在滑动时进入 PiP 模式。与通过 enterPictureInPictureMode 相比,这样可提供更流畅的动画。
  4. 使用 findActivity() 拨打 setPictureInPictureParams()。对 builder 调用 build() 并将其传入。

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(true)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}
VideoPlayer(pipModifier)

使用 setAspectRatio 设置 PiP 窗口的宽高比

如需设置 PiP 窗口的宽高比,您可以选择特定的宽高比,也可以使用播放器视频尺寸的宽度和高度。如果您使用的是 media3 播放器,请在设置宽高比之前检查播放器是否不为 null,以及播放器的视频大小是否不等于 VideoSize.UNKNOWN。

val context = LocalContext.current

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()
    if (shouldEnterPipMode && player != null && player.videoSize != VideoSize.UNKNOWN) {
        val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect()
        builder.setSourceRectHint(sourceRect)
        builder.setAspectRatio(
            Rational(player.videoSize.width, player.videoSize.height)
        )
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(shouldEnterPipMode)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}

VideoPlayer(pipModifier)

如果您使用的是自定义播放器,请使用特定于您所用播放器的语法,在播放器的高度和宽度上设置宽高比。请注意,如果播放器在初始化期间调整大小,并且调整后的尺寸超出了宽高比的有效范围,您的应用将会崩溃。您可能需要在可以计算宽高比时添加检查,类似于 media3 播放器的处理方式。