Der Bild-im-Bild-Modus (PiB) ist ein spezieller Mehrfenstermodus, der hauptsächlich für die Videowiedergabe verwendet wird. Nutzer können sich ein Video in einem kleinen Fenster ansehen, das an einer Ecke des Bildschirms angepinnt ist, während sie zwischen Apps wechseln oder Inhalte auf dem Hauptbildschirm durchsuchen.
BiB nutzt die in Android 7.0 verfügbaren Mehrfenster-APIs, um angepinnten Video-Overlay-Fenster. Um BiB zu deiner App hinzuzufügen, musst du deine aktualisiere deine Aktivitäten nach Bedarf in den BiB-Modus und achte darauf, dass UI-Elemente werden ausgeblendet und die Videowiedergabe wird fortgesetzt, wenn sich die Aktivität im BiB-Modus befindet.
In dieser Anleitung wird beschrieben, wie du deiner App BiB in „Compose“ mit einem Video des Typs „Verfassen“ hinzufügen kannst Implementierung. Sieh dir diese besten Produkte in der App Socialite an. in der Praxis anwenden.
BiB für deine App einrichten
Führen Sie im Aktivitäts-Tag der Datei AndroidManifest.xml
folgende Schritte aus:
- Fügen Sie
supportsPictureInPicture
hinzu und setzen Sie es auftrue
, um zu erklären, dass Sie mit BiB in Ihrer App nutzen. configChanges
hinzufügen und festlegen auforientation|screenLayout|screenSize|smallestScreenSize
, um anzugeben, dass verarbeitet Ihre Aktivität Änderungen der Layoutkonfiguration. Auf diese Weise werden Ihre Aktivitäten wird nicht neu gestartet, wenn Layoutänderungen bei Übergängen im BiB-Modus erfolgen.<activity android:name=".SnippetsActivity" android:exported="true" android:supportsPictureInPicture="true" android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize" android:theme="@style/Theme.Snippets">
Gehen Sie in Ihrem Compose-Code so vor:
- Diese Erweiterung auf
Context
hinzufügen. Sie verwenden diese Erweiterung im Laufe des Leitfadens mehrmals, um auf die Aktivität zuzugreifen.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") }
BiB beim Verlassen der App für ältere Versionen vor Android 12 hinzufügen
Wenn du BiB für Nutzer vor Android 12 hinzufügen möchtest, verwende addOnUserLeaveHintProvider
. Folgen
So fügst du BiB für Nutzer vor Android 12 hinzu:
- Fügen Sie ein Versionsgate hinzu, sodass auf diesen Code nur in den Versionen O bis R zugegriffen wird.
- Verwenden Sie
DisposableEffect
mitContext
als Schlüssel. - Definieren Sie in
DisposableEffect
das Verhalten für denonUserLeaveHintProvider
wird durch eine Lambda-Funktion ausgelöst. Rufen Sie in der Lambda-FunktionenterPictureInPictureMode()
amfindActivity()
und übergeben Sie die KartePictureInPictureParams.Builder().build()
- Füge mit
findActivity()
addOnUserLeaveHintListener
hinzu und übergib die Lambda-Funktion. - Fügen Sie in
onDispose
mitfindActivity()
removeOnUserLeaveHintListener
hinzu und die Lambda übergeben.
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") }
PiP-Unterstützung für den App-Hintergrund für Android 12 und höher hinzufügen
Ab Android 12 wird der PictureInPictureParams.Builder
über einen Modifikator hinzugefügt, der an den Videoplayer der App übergeben wird.
- Erstellen Sie eine
modifier
und rufen SieonGloballyPositioned
darauf auf. Das Layout werden in einem späteren Schritt verwendet. - Erstellen Sie eine Variable für
PictureInPictureParams.Builder()
. - Füge eine
if
-Anweisung hinzu, um zu prüfen, ob das SDK S oder höher ist. Wenn ja, fügen SiesetAutoEnterEnabled
an den Builder und legen Sie ihn auftrue
fest, um BiB zu verwenden wenn Sie wischen. Dadurch wird eine flüssigere Animation erzielt als bei der Verwendung vonenterPictureInPictureMode
. findActivity()
verwenden, umsetPictureInPictureParams()
aufzurufen.build()
anrufen unter und übergeben es an diebuilder
.
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)
BiB über eine Schaltfläche hinzufügen
Um in den BiB-Modus über eine Schaltfläche zu wechseln, rufen Sie
enterPictureInPictureMode()
am findActivity()
.
Die Parameter wurden bereits durch vorherige Aufrufe der
PictureInPictureParams.Builder
, Sie müssen also keine neuen Parameter festlegen.
für den Builder. Wenn Sie jedoch die Parameter der Schaltfläche
können Sie sie hier festlegen.
val context = LocalContext.current Button(onClick = { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.findActivity().enterPictureInPictureMode( PictureInPictureParams.Builder().build() ) } else { Log.i(PIP_TAG, "API does not support PiP") } }) { Text(text = "Enter PiP mode!") }
UI im BiB-Modus verwalten
Im BiB-Modus erscheint die gesamte Benutzeroberfläche Ihrer App im BiB-Fenster, es sei denn, Sie wie deine UI im BiB-Modus und aus dem BiB-Modus aussehen soll.
Zuerst müssen Sie wissen, ob sich Ihre App im BiB-Modus befindet. Sie können
OnPictureInPictureModeChangedProvider
.
Der folgende Code gibt an, ob sich Ihre App im BiB-Modus befindet.
@Composable fun rememberIsInPipMode(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val activity = LocalContext.current.findActivity() var pipMode by remember { mutableStateOf(activity.isInPictureInPictureMode) } DisposableEffect(activity) { val observer = Consumer<PictureInPictureModeChangedInfo> { info -> pipMode = info.isInPictureInPictureMode } activity.addOnPictureInPictureModeChangedListener( observer ) onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) } } return pipMode } else { return false } }
Jetzt können Sie mit rememberIsInPipMode()
festlegen, welche UI-Elemente angezeigt werden sollen, wenn die App in den PiP-Modus wechselt:
val inPipMode = rememberIsInPipMode() Column(modifier = modifier) { // This text will only show up when the app is not in PiP mode if (!inPipMode) { Text( text = "Picture in Picture", ) } VideoPlayer() }
Sorge dafür, dass deine App zur richtigen Zeit in den BiB-Modus wechselt.
In den folgenden Situationen sollte Ihre App nicht in den BiB-Modus wechseln:
- Wenn das Video angehalten oder pausiert ist.
- Sie befinden sich auf einer anderen Seite der App als der Videoplayer.
Wenn du festlegen möchtest, wann deine App in den BiB-Modus wechselt, füge eine Variable hinzu, die den Status verfolgt
des Videoplayers mit mutableStateOf
.
Status abhängig davon wechseln, ob das Video abgespielt wird
Zum Umschalten des Status abhängig davon, ob der Videoplayer abgespielt wird, fügen Sie einen Listener auf Videoplayer. Ändern Sie den Status der Statusvariablen je nachdem, ob der Spieler gerade spielt oder nicht:
player.addListener(object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { shouldEnterPipMode = isPlaying } })
Status je nach Veröffentlichung des Players wechseln
Wenn der Player freigegeben wird, setze deine Statusvariable auf false
:
fun releasePlayer() { shouldEnterPipMode = false }
Mit dem Status kannst du festlegen, ob der BiB-Modus aktiviert wird (vor Android 12)
- Da beim Hinzufügen von BiB vor 12 ein
DisposableEffect
verwendet wird, musst du ein eine neue Variable vonrememberUpdatedState
, wobeinewValue
als Ihr state-Variable auf. So wird sichergestellt, dass die aktualisierte Version imDisposableEffect
In der Lambda, die das Verhalten definiert, wenn das
OnUserLeaveHintListener
ausgelöst wird, fügen Sie eineif
-Anweisung mit der Zustandsvariable um den Aufruf vonenterPictureInPictureMode()
:val currentShouldEnterPipMode by rememberUpdatedState(newValue = shouldEnterPipMode) 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 = { if (currentShouldEnterPipMode) { 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") }
Mit dem Status definieren, ob der BiB-Modus aktiviert wird (ab Android 12)
Übergeben Sie Ihre Zustandsvariable an setAutoEnterEnabled
, sodass Ihre App nur
BiB-Modus zur richtigen Zeit:
val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() // Add autoEnterEnabled for versions S and up if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { builder.setAutoEnterEnabled(shouldEnterPipMode) } context.findActivity().setPictureInPictureParams(builder.build()) } VideoPlayer(pipModifier)
Mit setSourceRectHint
eine flüssige Animation implementieren
Die setSourceRectHint
API erstellt eine flüssigere Animation bei der Eingabe von BiB
. Unter Android 12 und höher wird außerdem eine flüssigere Animation beim Beenden des PiP-Modus erstellt.
Fügen Sie diese API dem PiP-Builder hinzu, um den Bereich der Aktivität anzugeben, der nach dem Wechsel zu PiP sichtbar ist.
- Fügen Sie dem
builder
nur dannsetSourceRectHint()
hinzu, wenn der Bundesstaat Folgendes definiert: App in den BiB-Modus wechseln. Dadurch wird vermieden, dasssourceRect
berechnet wird, wenn die App BiB nicht eingegeben werden muss. - Um den Wert
sourceRect
festzulegen, verwenden Sie die angegebenenlayoutCoordinates
aus der FunktiononGloballyPositioned
für den Modifikator. - Rufe
setSourceRectHint()
imbuilder
auf und übergebe die VariablesourceRect
.
val context = LocalContext.current val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() if (shouldEnterPipMode) { val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect() builder.setSourceRectHint(sourceRect) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { builder.setAutoEnterEnabled(shouldEnterPipMode) } context.findActivity().setPictureInPictureParams(builder.build()) } VideoPlayer(pipModifier)
Mit setAspectRatio
das Seitenverhältnis des BiB-Fensters festlegen
Um das Seitenverhältnis des BiB-Fensters festzulegen, können Sie entweder ein bestimmtes
Seitenverhältnis oder die Breite und Höhe der Videogröße des Players an. Wenn Sie
dass Sie einen media3-Player verwenden, vergewissern Sie sich, dass der Wert
nicht null ist und dass die
Videogröße nicht gleich VideoSize.UNKNOWN
ist, bevor Sie das Seitenverhältnis festlegen
Seitenverhältnis.
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)
Wenn du einen benutzerdefinierten Player verwendest, lege das Seitenverhältnis auf der Höhe des Players fest. und Breite unter Verwendung der für Ihren Player spezifischen Syntax festlegen. Wenn Ihr Spieler ändert seine Größe während der Initialisierung, wenn sie außerhalb der gültigen Grenzen von oder das Seitenverhältnis haben, stürzt die App ab. Eventuell müssen Sie im Bereich wann das Seitenverhältnis berechnet werden kann, ähnlich wie bei einem „media“.3 Player.
Fernaktionen hinzufügen
Wenn Sie Ihrem BiB-Fenster Steuerelemente (Wiedergabe, Pause usw.) hinzufügen möchten, erstellen Sie ein
RemoteAction
für jedes Steuerelement, das Sie hinzufügen möchten.
- Fügen Sie Konstanten für die Übertragungssteuerung hinzu:
// Constant for broadcast receiver const val ACTION_BROADCAST_CONTROL = "broadcast_control" // Intent extras for broadcast controls from Picture-in-Picture mode. const val EXTRA_CONTROL_TYPE = "control_type" const val EXTRA_CONTROL_PLAY = 1 const val EXTRA_CONTROL_PAUSE = 2
- Erstelle eine Liste von
RemoteActions
für die Steuerelemente in deinem BiB-Fenster. - Fügen Sie als Nächstes einen
BroadcastReceiver
hinzu und überschreiben SieonReceive()
, um den Aktionen der einzelnen Schaltflächen. Verwenden SieDisposableEffect
, um den Empfänger und die Remote-Aktionen zu registrieren. Wenn der Player entsorgt wird, hebe die Registrierung des Empfänger.@RequiresApi(Build.VERSION_CODES.O) @Composable fun PlayerBroadcastReceiver(player: Player?) { val isInPipMode = rememberIsInPipMode() if (!isInPipMode || player == null) { // Broadcast receiver is only used if app is in PiP mode and player is non null return } val context = LocalContext.current DisposableEffect(player) { val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if ((intent == null) || (intent.action != ACTION_BROADCAST_CONTROL)) { return } when (intent.getIntExtra(EXTRA_CONTROL_TYPE, 0)) { EXTRA_CONTROL_PAUSE -> player.pause() EXTRA_CONTROL_PLAY -> player.play() } } } ContextCompat.registerReceiver( context, broadcastReceiver, IntentFilter(ACTION_BROADCAST_CONTROL), ContextCompat.RECEIVER_NOT_EXPORTED ) onDispose { context.unregisterReceiver(broadcastReceiver) } } }
- Übergeben Sie eine Liste Ihrer Remote-Aktionen an den
PictureInPictureParams.Builder
:val context = LocalContext.current val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() builder.setActions( listOfRemoteActions() ) 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(modifier = pipModifier)
Nächste Schritte
In diesem Leitfaden haben Sie die Best Practices zum Hinzufügen von PiP in Compose sowohl vor als auch nach Android 12 kennengelernt.
- In der Socialite-App findest du die Best Practices der BiB in Aktion verfassen
- Weitere Informationen finden Sie in den Designrichtlinien für Picture-in-Picture-Videos.