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

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 播放器的处理方式。