Biblioteka Jetpack Picture-in-Picture (PiP) to uproszczone i niezawodne rozwiązanie, które umożliwia deweloperom aplikacji na Androida implementowanie funkcji PiP, zwłaszcza w przypadku aplikacji do odtwarzania multimediów, komunikacji wideo i nawigacji. Dzięki ujednoliconemu interfejsowi API biblioteka pomaga wyeliminować powtarzalny kod, typowe błędy w aplikacji i poprawić ogólną jakość obsługi PiP.
Biblioteka Jetpack PiP ułatwia korzystanie z dotychczasowych interfejsów API PiP, rozwiązując kilka kluczowych problemów i niespójności w ekosystemie Androida:
- Fragmentacja systemu operacyjnego: biblioteka automatycznie obsługuje różnice w wywołaniach interfejsu API PiP
w różnych wersjach Androida, np. używa funkcji
enterPictureInPictureModew wersjach starszych niż Android 12 iisAutoEnterEnabledw nowszych, dzięki czemu deweloperzy nie muszą zarządzać różnicami między wersjami . - Nieprawidłowe parametry PiP: biblioteka zapewnia ujednolicone rozwiązanie do prawidłowego
ustawiania parametrów PiP, np.
setSourceRectHint, aby tworzyć płynne i wysokiej jakości animacje podczas odtwarzania multimediów. - Ujednolicone wywołania zwrotne stanu PiP: biblioteka łączy wywołania zwrotne
onPictureInPictureModeChangedionPictureInPictureUiStateChangedw jeden ujednolicony interfejs wywołania zwrotnego (PictureInPictureDelegate.OnPictureInPictureEventListener), co upraszcza zarządzanie stanem i interfejsem. - Ograniczenie powtarzalnego kodu: biblioteka zmniejsza ilość powtarzalnego kodu, oferując predefiniowane zestawy
RemoteActionsna potrzeby typowych przypadków użycia, takich jak elementy sterujące odtwarzaniem i działania związane z rozmowami wideo. - Przyszłościowe rozwiązanie: kolejne funkcje PiP są udostępniane za pomocą biblioteki Jetpack, co pozwala użytkownikom uzyskiwać dostęp do dodatkowych funkcji przy minimalnym wysiłku.
Przepływ migracji
Określ kategorię przypadku użycia aplikacji i starszą logikę PiP:
Kategorie: odtwarzanie wideo, nawigacja lub rozmowa wideo.
Starsza logika PiP do zidentyfikowania:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Konfiguracja pliku AndroidManifest.xml
Upewnij się, że aktywność wchodząca w tryb PiP deklaruje obsługę w pliku AndroidManifest.xml z niezbędnymi elementami configChanges, aby zapobiec niepotrzebnym restartom:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Konfiguracja środowiska
Dodaj wymagane zależności do pliku build.gradle:
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
W przypadku zależności użyj najnowszych bibliotek AndroidX i zapoznaj się z informacjami na stronie wersji.
4. Wybór i inicjowanie szablonu
Wybierz szablon implementacji, który najlepiej pasuje do przypadku użycia aplikacji:
- Nawigacja i rozmowa wideo:
BasicPictureInPicture; płynna zmiana rozmiaru zwykle nie jest obsługiwana i nie potrzebujesz wskazówki dotyczącej prostokąta źródłowego. - Odtwarzanie wideo:
VideoPlaybackPictureInPicture; automatycznie śledzi granice widoku odtwarzacza na potrzeby wskazówki dotyczącej prostokąta źródłowego i domyślnie włącza płynną zmianę rozmiaru.
Aby zastosować bibliotekę Jetpack, zastąp dotychczasową niestandardową implementację PiP interfejsami API biblioteki Jetpack. Złożoność i koszt wdrożenia będą się różnić w zależności od bieżącej implementacji aplikacji.
W sekcjach poniżej opisujemy niektóre typowe przypadki użycia PiP i niezbędne kroki implementacji:
Nawigacja
Aplikacja informuje bibliotekę o stanie aktywnym lub nieaktywnym nawigacji i ustawia współczynnik proporcji. Biblioteka Jetpack zajmie się resztą.
Najważniejsze różnice:
- Nie musisz rozróżniać automatycznego i starszego trybu wchodzenia w tryb PiP po stronie aplikacji.
- Ujednolicone interfejsy wywołań zwrotnych.
- Nowy konstruktor
PictureInPictureParamsna potrzeby zgodności wstecznej.
Rozmowa wideo
Aplikacja informuje bibliotekę o stanie aktywnym lub nieaktywnym połączenia i ustawia współczynnik proporcji.
Najważniejsze różnice:
- Nie musisz rozróżniać automatycznego i starszego trybu wchodzenia w tryb PiP po stronie aplikacji.
- Ujednolicone interfejsy wywołań zwrotnych.
- Nowy konstruktor
PictureInPictureParamsna potrzeby zgodności wstecznej. - Ustandaryzowane ikony działań w przypadku rozmów wideo.
5. Migracja kodu
- Logika wejścia: zastąp logikę specyficzną dla interfejsu API, np.
setAutoEnterEnabledw przypadku Androida 12 i nowszych wersji lubonUserLeaveHintw przypadku Androida 11 i starszych wersji funkcjąsetEnabled. Wywołuj tę funkcję za każdym razem, gdy zmieni się stan kwalifikacji do PiP. - Wywołania zwrotne: połącz wywołania zwrotne
onPictureInPictureModeChanged(przełączanie układu) ionPictureInPictureUiStateChanged(animacja/stany) w jedno wywołanie zwrotne oparte na zdarzeniachonPictureInPictureEvent. - Działania i parametry: aktualizuj parametry za pomocą funkcji
setActionsisetAspectRatiow instancji szablonu za każdym razem, gdy się zmienią. - Specjalna obsługa wideo: w przypadku aplikacji wideo użyj funkcji
setPlayerViewaby zautomatyzować aktualizacje wskazówki dotyczącej prostokąta źródłowego i zapewnić płynne przejścia. ` ### 6. Czyszczenie
W przypadku VideoPlaybackPictureInPicture wywołaj funkcję close w
onDispose lub onDestroy, aby zwolnić zasoby, takie jak śledzenie widoków.
Wzorce implementacji referencyjnej
Przykłady implementacji.
Nawigacja i rozmowa wideo
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 */ } } } }
Odtwarzanie wideo
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() } } } }