Die Jetpack-Bibliothek für Bild-im-Bild (BiB) bietet eine optimierte und robuste Lösung für Android-App-Entwickler, um die BiB-Funktionalität zu implementieren, insbesondere für Apps zur Medienwiedergabe, Videokommunikation und Navigation. Durch die Bereitstellung einer einheitlichen API trägt die Bibliothek dazu bei, Boilerplate-Code und häufige In-App-Fehler zu vermeiden und die Gesamtqualität der PiP-Benutzerfreundlichkeit zu verbessern.
Die PiP-Jetpack-Bibliothek vereinfacht die vorhandenen PiP-APIs, indem sie mehrere wichtige Herausforderungen und Inkonsistenzen im Android-Ökosystem angeht:
- OS-Fragmentierung: 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 BiB-Parameter: Es bietet eine einheitliche Lösung zum korrekten Festlegen von BiB-Parametern, z. B.
setSourceRectHint, um während der Medienwiedergabe flüssige und hochwertige Animationen zu erstellen. - Einheitliche PiP-Status-Callbacks:
onPictureInPictureModeChangedundonPictureInPictureUiStateChangedwerden in einer einzigen, einheitlichen Callback-Schnittstelle (PictureInPictureDelegate.OnPictureInPictureEventListener) zusammengefasst, um die Status- und UI-Verwaltung zu vereinfachen. - Weniger Boilerplate-Code: Die Bibliothek reduziert die Menge an sich wiederholendem Boilerplate-Code, indem sie vordefinierte Sets von
RemoteActionsfür gängige Anwendungsfälle wie Wiedergabesteuerung und Videoanrufaktionen bietet. - Zukunftssicherheit: Weitere BiB-Funktionen werden über die Jetpack-Bibliothek bereitgestellt, sodass Nutzer mit minimalem bis gar keinem Aufwand auf zusätzliche Funktionen zugreifen können.
Migrationsworkflow
Kategorie des Anwendungsfalls und alte PiP-Logik der App ermitteln:
Kategorien:Videowiedergabe, Navigation oder Videoanruf.
Alte PiP-Logik zur Identifizierung:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. AndroidManifest-Konfiguration
Achte darauf, dass die Aktivität, die in den PiP-Modus wechselt, die Unterstützung in AndroidManifest.xml mit dem erforderlichen configChanges deklariert, 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 build.gradle die erforderlichen Abhängigkeiten 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") }
Verwenden Sie die neuesten AndroidX-Bibliotheken für die Abhängigkeiten. Informationen dazu finden Sie auf der Seite Releases.
4. Vorlage auswählen und initialisieren
Wählen Sie die Implementierungsvorlage aus, die am besten zum Anwendungsfall der App passt:
- Navigation und Videoanruf:
BasicPictureInPicture; nahtlose Größenänderung wird in der Regel nicht unterstützt und Sie benötigen keinen Hinweis auf das Quellrechteck. - Videowiedergabe:
VideoPlaybackPictureInPicture; verfolgt automatisch die Grenzen der Playeransicht für den Quellrechteck-Hinweis und ermöglicht standardmäßig eine nahtlose Größenanpassung.
Wenn Sie die Jetpack-Bibliothek verwenden möchten, müssen Sie Ihre vorhandene benutzerdefinierte PiP-Implementierung durch die Jetpack-Bibliotheks-APIs ersetzen. Die Komplexität und die Kosten der Umstellung hängen von der aktuellen Implementierung der App ab.
In den folgenden Abschnitten werden einige typische Anwendungsfälle für den Bild-im-Bild-Modus 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. Die Jetpack-Bibliothek übernimmt den Rest.
Wichtige Unterschiede
- Auf App-Seite muss nicht zwischen der automatischen Eingabe und der alten Eingabe unterschieden werden.
- Konsolidierte Callback-Schnittstellen.
- Neuer
PictureInPictureParams-Builder für die 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
- Auf App-Seite muss nicht zwischen der automatischen Eingabe und der alten Eingabe unterschieden werden.
- Konsolidierte Callback-Schnittstellen.
- Neuer
PictureInPictureParams-Builder für die Abwärtskompatibilität. - Standardisierte Aktionssymbole für Videoanrufe.
5. Codemigration
- Eingangslogik:Ersetzen Sie API-spezifische Logik wie
setAutoEnterEnabledfür Android 12 und höher oderonUserLeaveHintfür Android 11 und niedriger durchsetEnabled. Löse diesen Trigger immer dann aus, wenn sich der Status der Eignung für den BiB-Modus ändert. - Callbacks:
onPictureInPictureModeChanged(Layout-Umschaltung) undonPictureInPictureUiStateChanged(Animation/Status) werden in einem einheitlichen ereignisbasierten CallbackonPictureInPictureEventzusammengefasst. - Aktionen und Parameter:Aktualisieren Sie Parameter mit
setActionsundsetAspectRatioin der Vorlageninstanz, sobald sie sich ändern. - Sonderbehandlung von Videos:Verwenden Sie für Video-Apps
setPlayerView, um die Aktualisierung von Quellrechteck-Hinweisen zu automatisieren und für reibungslose Übergänge zu sorgen. ` ### 6. Bereinigen
Rufen Sie für VideoPlaybackPictureInPicture close in onDispose oder onDestroy auf, um Ressourcen wie View-Tracker freizugeben.
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() } } } }