Biblioteka Jetpack do obrazu w obrazie to uproszczone i solidne rozwiązanie dla deweloperów aplikacji na Androida, które umożliwia wdrażanie funkcji obrazu w obrazie, 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ść korzystania z funkcji obrazu w obrazie.
Biblioteka Jetpack PiP ułatwia korzystanie z dotychczasowych interfejsów API PiP, rozwiązując kilka kluczowych problemów i niezgodnoś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
enterPictureInPictureModeprzed Androidem 12 iisAutoEnterEnabledpo nim, więc programiści nie muszą zarządzać różnicami między wersjami. - Nieprawidłowe parametry PiP: 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: łączy
onPictureInPictureModeChangedionPictureInPictureUiStateChangedw jeden ujednolicony interfejs wywołania zwrotnego (PictureInPictureDelegate.OnPictureInPictureEventListener), co upraszcza zarządzanie stanem i interfejsem. - Ograniczenie ilości powtarzalnego kodu: biblioteka zmniejsza ilość powtarzalnego kodu, oferując predefiniowane zestawy
RemoteActionsdo typowych zastosowań, takich jak elementy sterujące odtwarzaniem i działania związane z rozmowami wideo. - Przyszłościowe rozwiązanie: kolejne funkcje PiP są dostarczane za pomocą biblioteki Jetpack, co pozwala użytkownikom uzyskiwać dostęp do dodatkowych funkcji przy minimalnym lub zerowym 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 identyfikacji:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Konfiguracja pliku AndroidManifest
Sprawdź, czy aktywność wchodząca w tryb obrazu w obrazie deklaruje obsługę w AndroidManifest.xml z odpowiednim configChanges, aby zapobiec niepotrzebnym ponownym uruchomieniom:
<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żywaj najnowszych bibliotek AndroidX. Informacje na ten temat znajdziesz na stronie wersji.
4. Wybór i inicjowanie szablonu
Wybierz szablon implementacji, który najlepiej pasuje do przypadku użycia aplikacji:
- Nawigacja i rozmowy wideo:
BasicPictureInPicture; zmiana rozmiaru bez przerw nie jest zwykle obsługiwana i nie potrzebujesz wskazówki dotyczącej prostokąta źródłowego. - Odtwarzanie filmu:
VideoPlaybackPictureInPicture; automatycznie śledzi granice widoku odtwarzacza w przypadku wskazówki dotyczącej prostokąta źródłowego i domyślnie umożliwia płynną zmianę rozmiaru.
Aby wdrożyć bibliotekę Jetpack, zastąp istniejącą niestandardową implementację PiP interfejsami API biblioteki Jetpack. Złożoność i koszt wdrożenia będą się różnić w zależności od obecnej implementacji aplikacji.
W sekcjach poniżej opisujemy typowe przypadki użycia trybu obrazu w obrazie i niezbędne kroki wdrożenia:
Nawigacja
Aplikacja informuje bibliotekę o stanie aktywności lub nieaktywności nawigacji i ustawia współczynnik proporcji. Resztą zajmie się biblioteka Jetpack.
Najważniejsze różnice:
- Nie musisz rozróżniać automatycznego i starszego sposobu wprowadzania danych po stronie aplikacji.
- Skonsolidowane interfejsy wywołań zwrotnych.
- Nowy konstruktor
PictureInPictureParamszapewniający zgodność wsteczną.
Rozmowa wideo
Aplikacja informuje bibliotekę o stanie połączenia (aktywne lub nieaktywne) i ustawia współczynnik proporcji.
Najważniejsze różnice:
- Nie musisz rozróżniać automatycznego i starszego sposobu wprowadzania danych po stronie aplikacji.
- Skonsolidowane interfejsy wywołań zwrotnych.
- Nowy konstruktor
PictureInPictureParamszapewniający zgodność wsteczną. - Standardowe 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, kodemsetEnabled. Wywołuj go za każdym razem, gdy zmieni się stan kwalifikacji do trybu obrazu w obrazie. - Wywołania zwrotne: połącz
onPictureInPictureModeChanged(przełączanie układu) ionPictureInPictureUiStateChanged(animacja/stany) w ujednolicone wywołanie zwrotne oparte na zdarzeniachonPictureInPictureEvent. - Działania i parametry: aktualizuj parametry za pomocą funkcji
setActionsisetAspectRatiow instancji szablonu, gdy tylko się zmienią.
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 filmu
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() } } } }