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

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 之前的版本添加了退出应用时的画中画功能

如需为 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: () -> Unit = {
            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 之后的版本添加了离开应用时的画中画功能

在 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][6],然后再设置宽高比。

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 播放器执行的操作类似。