Die Jetpack-Bibliothek für Bild im Bild (BiB) bietet Android-App-Entwicklern eine optimierte und robuste Lösung zum Implementieren der BiB-Funktion, insbesondere für Apps zur Medienwiedergabe, Videokommunikation und Navigation. Durch die Bereitstellung einer einheitlichen API hilft die Bibliothek, Boilerplate-Code und häufige In-App-Fehler zu vermeiden und die Qualität der PiP-Nutzererfahrung insgesamt zu verbessern.
Die PiP-Jetpack-Bibliothek erleichtert die Verwendung der vorhandenen PiP-APIs, indem sie mehrere wichtige Herausforderungen und Inkonsistenzen im Android-Ökosystem behebt:
- Fragmentierung des Betriebssystems: Die Bibliothek verarbeitet automatisch Unterschiede bei PiP-API-Aufrufen in verschiedenen Android-Versionen, z. B. die Verwendung von
enterPictureInPictureModevor Android 12 undisAutoEnterEnableddanach. Entwickler müssen sich also nicht um Versionsunterschiede kümmern. - Falsche PiP-Parameter: Es bietet eine einheitliche Lösung, um PiP-Parameter wie
setSourceRectHintkorrekt festzulegen und so flüssige und hochwertige Animationen während der Medienwiedergabe zu erstellen. - Einheitliche PiP-Status-Callbacks:
onPictureInPictureModeChangedundonPictureInPictureUiStateChangedwerden in einer einzigen, einheitlichen Callback-Schnittstelle (PictureInPictureDelegate.OnPictureInPictureEventListener) zusammengefasst, um die Status- und UI-Verwaltung zu vereinfachen. - Reduzierung von Boilerplate-Code: Die Bibliothek reduziert die Menge an sich wiederholendem Boilerplate-Code, indem sie vordefinierte Sätze von
RemoteActionsfür gängige Anwendungsfälle wie Wiedergabesteuerung und Videoanrufaktionen bietet. - Zukunftssicherheit: Weitere PiP-Funktionen werden über die Jetpack-Bibliothek bereitgestellt, sodass Nutzer mit minimalem oder gar keinem Aufwand auf zusätzliche Funktionen zugreifen können.
Migrationsworkflow
Ermitteln Sie die Anwendungsfallkategorie und die alte PiP-Logik der App:
Kategorien: Videowiedergabe, Navigation oder Videoanruf.
Ältere PiP-Logik zur Identifizierung:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. AndroidManifest-Konfiguration
Achte darauf, dass die Aktivität, die in den PiP-Modus wechselt, in AndroidManifest.xml mit den erforderlichen configChanges deklariert wird, um unnötige Neustarts zu vermeiden:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Umgebung einrichten
Fügen Sie die erforderlichen Abhängigkeiten zu build.gradle hinzu:
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
Verwende die neuesten AndroidX-Bibliotheken für die Abhängigkeiten und sieh dir die Seite Releases an, um diese Informationen zu erhalten.
4. Vorlagenauswahl und Initialisierung
Wählen Sie die Implementierungsvorlage aus, die am besten zum Anwendungsfall der App passt:
- Navigation und Videoanruf:
BasicPictureInPicture; die nahtlose Größenanpassung wird in der Regel nicht unterstützt und Sie benötigen keinen Hinweis auf das Quellrechteck. - Videowiedergabe:
VideoPlaybackPictureInPicture; verfolgt automatisch die Grenzen der Player-Ansicht für den Hinweis zum Quellrechteck und ermöglicht standardmäßig eine nahtlose Größenänderung.
Wenn Sie die Jetpack-Bibliothek verwenden möchten, ersetzen Sie Ihre vorhandene benutzerdefinierte PiP-Implementierung durch die Jetpack-Bibliotheks-APIs. Die Komplexität und die Kosten der Einführung hängen von der aktuellen Implementierung der App ab.
In den folgenden Abschnitten werden einige typische Anwendungsfälle von PiP und die erforderlichen Implementierungsschritte beschrieben:
Navigation
Die App informiert die Bibliothek über den aktiven oder inaktiven Status der Navigation und legt das Seitenverhältnis fest. Den Rest übernimmt die Jetpack-Bibliothek.
Wichtige Unterschiede
- Es ist nicht erforderlich, auf App-Seite zwischen automatischem und herkömmlichem Betreten zu unterscheiden.
- Konsolidierte Callback-Schnittstellen.
- Neuer
PictureInPictureParams-Builder für Abwärtskompatibilität.
Videoanruf
Die App informiert die Bibliothek über den aktiven oder inaktiven Status des Anrufs und legt das Seitenverhältnis fest.
Wichtige Unterschiede
- Es ist nicht erforderlich, auf App-Seite zwischen automatischem und herkömmlichem Betreten zu unterscheiden.
- Konsolidierte Callback-Schnittstellen.
- Neuer
PictureInPictureParams-Builder für Abwärtskompatibilität. - Standardisierte Aktionssymbole für Videoanrufe.
5. Codemigration
- Einstiegslogik:Ersetze API-spezifische Logik wie
setAutoEnterEnabledfür Android 12 und höher oderonUserLeaveHintfür Android 11 und niedriger durchsetEnabled. Lösen Sie dies immer aus, wenn sich der PiP-Berechtigungsstatus ändert. - Callbacks:
onPictureInPictureModeChanged(Layoutwechsel) undonPictureInPictureUiStateChanged(Animation/Zustände) werden in einem einheitlichen ereignisbasierten CallbackonPictureInPictureEventzusammengefasst. - Aktionen und Parameter: Aktualisieren Sie Parameter mit
setActionsundsetAspectRatioin der Vorlageninstanz, wenn sie sich ändern.
Referenzimplementierungsmuster
Beispiele für Implementierungen.
Navigation und Videoanruf
class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } } } }
Videowiedergabe
class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } } } @Composable fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { DisposableEffect(pipController) { onDispose { pipController.close() } } } }