Biblioteka Jetpack obrazu w obrazie to uproszczone i niezawodne rozwiązanie dla deweloperów aplikacji na Androida, które umożliwia implementację funkcji obrazu w obrazie, zwłaszcza w przypadku aplikacji do odtwarzania multimediów, komunikacji wideo i nawigacji. Dzięki udostępnieniu ujednoliconego interfejsu API biblioteka pomaga wyeliminować powtarzalny kod i typowe błędy w aplikacji oraz poprawić ogólną jakość korzystania z funkcji obrazu w obrazie.
Biblioteka Jetpack PiP ułatwia korzystanie z istniejących 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 obrazu w obrazie w różnych wersjach Androida, np. używa
enterPictureInPictureModeprzed Androidem 12 iisAutoEnterEnabledpo nim, dzięki czemu deweloperzy nie muszą zarządzać różnicami między wersjami. - Nieprawidłowe parametry obrazu w obrazie: zapewnia ujednolicone rozwiązanie do prawidłowego ustawiania parametrów obrazu w obrazie, np.
setSourceRectHint, aby tworzyć płynne i wysokiej jakości animacje podczas odtwarzania multimediów. - Ujednolicone wywołania zwrotne stanu PiP: konsoliduje
onPictureInPictureModeChangedionPictureInPictureUiStateChangedw jeden ujednolicony interfejs wywołania zwrotnego (PictureInPictureDelegate.OnPictureInPictureEventListener), co upraszcza zarządzanie stanem i interfejsem. - Mniej powtarzalnego kodu: biblioteka zmniejsza ilość powtarzalnego kodu, oferując wstępnie zdefiniowane 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 obrazu w obrazie są dostarczane przez bibliotekę Jetpack, co pozwala użytkownikom na dostęp do dodatkowych funkcji przy minimalnym wysiłku.
Przepływ migracji
Określ kategorię przypadku użycia aplikacji i starszą logikę obrazu w obrazie:
Kategorie: odtwarzanie filmów, nawigacja lub rozmowa wideo.
Starsza logika PiP do identyfikacji:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Konfiguracja pliku AndroidManifest
Upewnij się, że aktywność, która przechodzi w tryb obrazu w obrazie, deklaruje obsługę w pliku AndroidManifest.xml z odpowiednim elementem 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") }
Używaj najnowszych bibliotek AndroidX w zależnościach. Informacje o nich znajdziesz na stronie Wersje.
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 nie jest zwykle obsługiwana i nie potrzebujesz wskazówki dotyczącej prostokąta źródłowego. - Odtwarzanie filmów:
VideoPlaybackPictureInPictureautomatycznie śledzi granice widoku odtwarzacza, aby uzyskać wskazówkę dotyczącą prostokąta źródłowego, i domyślnie umożliwia płynną zmianę rozmiaru.
Aby zastosować bibliotekę Jetpack, zastąp dotychczasową niestandardową implementację obrazu w obrazie 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 poniższych sekcjach opisujemy niektóre typowe przypadki użycia obrazu w obrazie i niezbędne kroki implementacji:
Nawigacja
Aplikacja informuje bibliotekę o aktywnym lub nieaktywnym stanie nawigacji i ustawia format obrazu. Resztą zajmuje się biblioteka Jetpack.
Najważniejsze różnice:
- Nie trzeba rozróżniać automatycznego i starszego sposobu wprowadzania po stronie aplikacji.
- Skonsolidowane interfejsy wywołania zwrotnego.
- Nowy kreator
PictureInPictureParamszapewniający zgodność wsteczną.
Rozmowa wideo
Aplikacja informuje bibliotekę o aktywnym lub nieaktywnym stanie połączenia i ustawia format obrazu.
Najważniejsze różnice:
- Nie trzeba rozróżniać automatycznego i starszego sposobu wprowadzania po stronie aplikacji.
- Skonsolidowane interfejsy wywołania zwrotnego.
- Nowy kreator
PictureInPictureParamszapewniający zgodność wsteczną. - Standaryzowane ikony działań podczas rozmowy wideo.
5. Migracja kodu
- Logika wejścia: zastąp logikę interfejsu API, np.
setAutoEnterEnabledw przypadku Androida 12 i nowszych wersji lubonUserLeaveHintw przypadku Androida 11 i starszych wersji, za pomocąsetEnabled. Uruchamiaj to zdarzenie za każdym razem, gdy zmieni się stan uprawnień do korzystania z trybu obrazu w obrazie. - Wywołania zwrotne: skonsoliduj
onPictureInPictureModeChanged(przełączanie układu) ionPictureInPictureUiStateChanged(animacja/stany) w ujednolicone wywołanie zwrotne oparte na zdarzeniachonPictureInPictureEvent. - Działania i parametry: aktualizuj parametry za pomocą
setActionsisetAspectRatiow instancji szablonu, gdy tylko ulegną zmianie.
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() } } } }