Począwszy od Androida w wersji 8.0 (interfejs API na poziomie 26), Android pozwala uruchamiać aktywności w trybie obraz w obrazie (PiP). Tryb PiP to specjalny tryb wielookienkowy, który jest używany głównie do odtwarzania filmów. Pozwala użytkownikowi oglądać film w małym oknie przypiętym do rogu ekranu, a jednocześnie przełączać inne aplikacje lub wyszukiwać treści na głównym ekranie.
Tryb PiP korzysta z interfejsów API dla wielu okien dostępnych w Androidzie 7.0, aby wyświetlać przypięte okno z filmem. Aby dodać PiP do aplikacji, musisz zarejestrować czynności, które obsługują PiP, odpowiednio przełączyć czynności w tryb PiP i zadbać o to, aby elementy interfejsu były ukryte, a odtwarzanie filmu było kontynuowane, gdy czynność jest w trybie PiP.
Okno PiP pojawi się na najwyższej warstwie ekranu w rogu wybranym przez system.
Tryb PiP jest też obsługiwany na zgodnych urządzeniach z Androidem TV z Androidem 14 (poziom interfejsu API 34) lub nowszym. Chociaż istnieje wiele podobieństw, podczas korzystania z okna PIP na telewizorze należy wziąć pod uwagę dodatkowe kwestie.
Jak użytkownicy mogą wchodzić w interakcję z oknem PiP
Użytkownicy mogą przeciągać okno PiP w inne miejsce. Od Androida 12 użytkownicy mogą też:
Kliknij okno, aby wyświetlić przełącznik pełnoekranowy, przycisk Zamknij, przycisk Ustawienia oraz niestandardowe działania udostępniane przez aplikację (np. elementy sterujące odtwarzaniem).
Kliknij okno dwukrotnie, aby przełączyć się między bieżącym a maksymalnym rozmiarem PiP lub między minimalnym a maksymalnym rozmiarem PiP. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna spowoduje jego zminimalizowanie, a na odwrót.
Ukryj okno, przeciągając je do lewej lub prawej krawędzi. Aby odzyskać okno, kliknij widoczną część ukrytego okna lub przeciągnij je.
Zmieniaj rozmiar okna PiP za pomocą funkcji „Ściągnij lub rozciągnij palce”.
Twoja aplikacja określa, kiedy bieżąca aktywność przejdzie w tryb obrazu w powietrzu. Oto kilka przykładów:
Aktywność może przejść w tryb obrazu w powietrzu, gdy użytkownik kliknie przycisk ekranu głównego lub przesunie palcem w górę, aby otworzyć ekran główny. Dzięki temu Mapy Google mogą wyświetlać wskazówki, gdy użytkownik wykonuje jednocześnie inną czynność.
Aplikacja może przenieść film do trybu PiP, gdy użytkownik przejdzie z niego, aby przeglądać inne treści.
Aplikacja może przełączyć film do trybu obrazu w obrazie, gdy użytkownik ogląda koniec odcinka treści. Ekran główny zawiera informacje promocyjne lub podsumowanie dotyczące następnego odcinka serii.
Aplikacja może umożliwiać użytkownikom dodawanie do kolejki dodatkowych treści podczas oglądania filmu. Film będzie nadal odtwarzany w trybie obrazu w obrazie, a na ekranie głównym wyświetli się aktywność związana z wyborem treści.
Deklarowanie obsługi obrazu w powiększeniu
Domyślnie system nie obsługuje automatycznie trybu PiP w przypadku aplikacji. Jeśli chcesz obsługiwać tryb obrazu w powietrzu w swojej aplikacji, zarejestruj aktywność związaną z wideo w pliku manifestu, ustawiając wartość android:supportsPictureInPicture
na true
. Pamiętaj też, aby w swojej aktywności uwzględnić zmiany konfiguracji układu, aby nie uruchamiała się ponownie podczas zmiany układu podczas przełączania do trybu PiP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Przełączanie aktywności do trybu obrazu w oknie
Od Androida 12 możesz przełączyć aktywność do trybu PiP, ustawiając flagę setAutoEnterEnabled
na true
. Dzięki temu ustawieniu aktywność automatycznie przełącza się na tryb PiP w razie potrzeby bez konieczności wywoływania funkcji enterPictureInPictureMode()
w onUserLeaveHint
. Dodatkową zaletą jest to, że przejścia są płynniejsze. Więcej informacji znajdziesz w artykule Ułatwianie płynnego przechodzenia do trybu obrazu w obrazie za pomocą nawigacji za pomocą gestów.
Jeśli kierujesz aplikację na Androida 11 lub niższego, aktywność musi wywołać funkcję enterPictureInPictureMode()
, aby przełączyć się do trybu obrazu w obrazie. Na przykład ten kod przełącza aktywność w tryb PiP, gdy użytkownik kliknie odpowiedni przycisk w interfejsie aplikacji:
Kotlin
override fun onActionClicked(action: Action) { if (action.id.toInt() == R.id.lb_control_picture_in_picture) { activity?.enterPictureInPictureMode() return } }
Java
@Override public void onActionClicked(Action action) { if (action.getId() == R.id.lb_control_picture_in_picture) { getActivity().enterPictureInPictureMode(); return; } ... }
Możesz użyć logiki, która przełączy aktywność w tryb PiP zamiast w tło. Na przykład Mapy Google przełączają się w tryb PiP, gdy użytkownik naciśnie przycisk Początek lub Ostatnie podczas nawigacji. Możesz to zrobić, zastępując onUserLeaveHint()
:
Kotlin
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Java
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Zalecane: zapewnij użytkownikom płynne przejście do trybu PiP
Android 12 wprowadził znaczne ulepszenia wizualne animowanych przejść między oknami pełnoekranowymi a oknami w trybie PiP. Zdecydowanie zalecamy wdrożenie wszystkich zalecanych zmian. Po ich wprowadzeniu będą one automatycznie skalowane na duże ekrany, takie jak składane urządzenia i tablety, bez konieczności wykonywania dodatkowych czynności.
Jeśli Twoja aplikacja nie zawiera odpowiednich aktualizacji, przejścia w trybie PiP nadal będą działać, ale animacje będą mniej dopracowane. Na przykład przejście z trybu pełnoekranowego do trybu PiP może spowodować zniknięcie okna PiP podczas przejścia, a następnie jego ponowne pojawienie się po zakończeniu przejścia.
Te zmiany obejmują:
- Ulepszone przechodzenie do trybu obrazu w oknie z użyciem gestów
- Ustawienie odpowiedniego
sourceRectHint
do włączania i wyłączania trybu obrazu w obrazie - Wyłączanie płynnego dostosowywania rozmiaru w przypadku treści innych niż filmy
Aby uzyskać informacje o umożliwianiu płynnych przejść, zapoznaj się z przykładem kodu Kotlin PictureInPicture na Androida.
Ułatwienie płynnego przechodzenia do trybu obrazu w obrębie obrazu podczas korzystania z nawigacji za pomocą gestów
Od Androida 12 flaga setAutoEnterEnabled
zapewnia znacznie płynniejszą animację podczas przechodzenia do treści wideo w reżymie obrazu w obrazie za pomocą gestów nawigacyjnych – np. gdy przesuniesz palcem w górę, aby wrócić do ekranu głównego z trybu pełnoekranowego.
Aby wprowadzić tę zmianę, wykonaj podane niżej czynności i jako punkt odniesienia użyj tego przykładu:
Aby utworzyć
PictureInPictureParams.Builder
, użyj elementusetAutoEnterEnabled
:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Zadzwoń pod numer
setPictureInPictureParams
, aby poinformować o aktualnym staniePictureInPictureParams
. Aplikacja nie czeka na wywołanieonUserLeaveHint
(jak w Androidzie 11).Możesz na przykład wywołać funkcję
setPictureInPictureParams
podczas pierwszego odtwarzania i przy każdym kolejnym odtwarzaniu, jeśli zmieni się format obrazu.Zadzwoń do
setAutoEnterEnabled(false)
, ale tylko wtedy, gdy jest to konieczne. Na przykład prawdopodobnie nie chcesz włączyć trybu PIP, jeśli bieżące odtwarzanie jest wstrzymane.
Ustaw odpowiednią sourceRectHint
na potrzeby włączania i wyłączania trybu obrazu w obrazie
Po wprowadzeniu funkcji PiP w Androidzie 8.0 setSourceRectHint
określała obszar aktywności widoczny po przejściu do obrazu w obrazie, na przykład obszar widoku filmu w odtwarzaczu.
W Androidzie 12 system używa sourceRectHint
do implementowania znacznie płynniejszej animacji zarówno podczas wchodzenia do trybu PiP, jak i wychodzenia z niego.
Aby prawidłowo ustawić sourceRectHint
na potrzeby wchodzenia do trybu obrazu w pliku i z niego wychodzenia:
Utwórz
PictureInPictureParams
, używając odpowiednich ograniczeń jakosourceRectHint
. Zalecamy też dołączenie do odtwarzacza wideo listenera zmiany układu:Kotlin
val mOnLayoutChangeListener = OnLayoutChangeListener { v: View?, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop: Int, newRight: Int, newBottom: Int -> val sourceRectHint = Rect() mYourVideoView.getGlobalVisibleRect(sourceRectHint) val builder = PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) setPictureInPictureParams(builder.build()) } mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
Java
private final View.OnLayoutChangeListener mOnLayoutChangeListener = (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> { final Rect sourceRectHint = new Rect(); mYourVideoView.getGlobalVisibleRect(sourceRectHint); final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint); setPictureInPictureParams(builder.build()); }; mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
W razie potrzeby zaktualizuj
sourceRectHint
przed rozpoczęciem przez system przejścia wyjścia. Gdy system ma zamknąć tryb PiP, hierarchia widoku aktywności jest wyświetlana w ramach konfiguracji docelowej (np. pełny ekran). Aplikacja może dołączyć do widoku głównego lub docelowego (np. widoku odtwarzacza wideo) detektor zmiany układu, aby wykryć zdarzenie i zaktualizowaćsourceRectHint
przed rozpoczęciem animacji.Kotlin
// Listener is called immediately after the user exits PiP but before animating. playerView.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. val sourceRectHint = Rect() playerView.getGlobalVisibleRect(sourceRectHint) setPictureInPictureParams( PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build() ) } }
Java
// Listener is called right after the user exits PiP but before animating. playerView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) { // The playerView's bounds changed, update the source hint rect to // reflect its new bounds. final Rect sourceRectHint = new Rect(); playerView.getGlobalVisibleRect(sourceRectHint); setPictureInPictureParams( new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) .build()); } });
Wyłączanie płynnego dostosowywania rozmiaru w przypadku treści innych niż filmy
Android 12 wprowadza flagę setSeamlessResizeEnabled
, która zapewnia płynniejszą animację przejścia podczas zmiany rozmiaru treści niewideo w oknie PiP. Wcześniej zmiana rozmiaru treści innych niż wideo w oknie PiP mogła powodować nieprzyjemne artefakty wizualne.
Aby wyłączyć bezszwonne dostosowywanie rozmiaru w przypadku treści innych niż filmy:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build());
Obsługa interfejsu podczas korzystania z PIP
Gdy aktywność wchodzi w tryb obraz w obrazie lub z niego wychodzi, system wywołuje Activity.onPictureInPictureModeChanged()
lub Fragment.onPictureInPictureModeChanged()
.
Android 15 wprowadza zmiany, które zapewniają jeszcze płynniejsze przechodzenie do trybu obrazu w płótnie. Jest to korzystne w przypadku aplikacji, które mają elementy UI nałożone na główny interfejs użytkownika i przechodzą do trybu PiP.
Deweloperzy używają wywołania zwrotnego onPictureInPictureModeChanged()
do definiowania logiki, która przełącza widoczność nałożonych elementów interfejsu.
To wywołanie zwrotne jest wywoływane po zakończeniu animacji wchodzenia lub wychodzenia z obrazu w obrazie.
Począwszy od Androida 15 klasa PictureInPictureUiState
zawiera nowy stan.
W tym nowym stanie interfejsu aplikacje kierowane na Androida 15 obserwują wywołanie funkcji Activity#onPictureInPictureUiStateChanged()
z isTransitioningToPip()
, gdy tylko rozpocznie się animacja PiP.
W trybie obrazu w obrazie wiele elementów interfejsu nie jest istotnych dla aplikacji. Dotyczy to na przykład widoków lub układu zawierających informacje takie jak sugestie, nadchodzące filmy, oceny i tytuły. Gdy aplikacja przejdzie w tryb PiP, użyj wywołania zwrotnego onPictureInPictureUiStateChanged()
, aby ukryć te elementy interfejsu. Gdy aplikacja przejdzie z okna PiP do trybu pełnoekranowego, użyj wywołania zwrotnego onPictureInPictureModeChanged()
, aby odsłonić te elementy, jak pokazano w tych przykładach:
Kotlin
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Java
@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Kotlin
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Java
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Szybkie przełączanie widoczności nieistotnych elementów interfejsu (w przypadku okna PiP) pomaga zapewnić płynniejszą i bezmigotnościową animację otwierania PiP.
Zastąp te funkcje zwracające wartości, aby ponownie narysować elementy interfejsu użytkownika aktywności. Pamiętaj, że w trybie obrazu w obrazie Twoja aktywność jest wyświetlana w małym oknie. Użytkownicy nie mogą wchodzić w interakcje z elementami interfejsu aplikacji, gdy jest ona w trybie obrazu w obrazie, a szczegóły małych elementów interfejsu mogą być niewidoczne. Najlepsze wrażenia użytkownika zapewniają działania związane z odtwarzaniem filmów z minimalnym interfejsem.
Jeśli Twoja aplikacja musi wykonywać niestandardowe działania w ramach PiP, na tej stronie przeczytaj sekcję Dodawanie elementów sterujących. Usuń inne elementy interfejsu, zanim aktywność przejdzie do obrazu w obrazie, i przywróć je, gdy aktywność znów będzie wyświetlana na pełnym ekranie.
Dodaj elementy sterujące
Okno PiP może wyświetlać elementy sterujące, gdy użytkownik otworzy menu okna (klikając okno na urządzeniu mobilnym lub wybierając menu za pomocą pilota do telewizora).
Jeśli aplikacja ma aktywną sesję multimedialną, pojawią się elementy sterujące odtwarzaniem, wstrzymywaniem, przewijaniem do przodu i wstecz.
Możesz też określić działania niestandardowe w sposób jawny, tworząc element PictureInPictureParams
za pomocą elementu PictureInPictureParams.Builder.setActions()
przed przejściem do trybu obrazu w obrazie i przekazując parametry podczas przechodzenia do tego trybu za pomocą elementu enterPictureInPictureMode(android.app.PictureInPictureParams)
lub setPictureInPictureParams(android.app.PictureInPictureParams)
.
Zachowaj ostrożność. Jeśli spróbujesz dodać więcej niż getMaxNumPictureInPictureActions()
,
będziesz mieć tylko maksymalną liczbę.
Kontynuowanie odtwarzania filmu w trybie PiP
Gdy aktywność przejdzie do trybu PiP, system wstrzyma aktywność i wywoła metodę onPause()
tej aktywności. Odtwarzanie filmu nie powinno być wstrzymywane. Jeśli aktywność zostanie wstrzymana podczas przechodzenia do trybu PiP, odtwarzanie powinno być kontynuowane.
W Androidzie 7.0 i nowszych należy wstrzymać i wznowić odtwarzanie filmu, gdy system wywoła metodę onStop()
lub onStart()
. Dzięki temu nie musisz sprawdzać, czy aplikacja jest w trybie obrazu w obrazie w onPause()
i wyraźnie kontynuować odtwarzania.
Jeśli flaga setAutoEnterEnabled
nie ma wartości true
i chcesz wstrzymać odtwarzanie w implementacji onPause()
, sprawdź tryb PiP, wywołując funkcję isInPictureInPictureMode()
, i odpowiednio obsłuż odtwarzanie. Przykład:
Kotlin
override fun onPause() { super.onPause() // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode) { // Continue playback. } else { // Use existing playback logic for paused activity behavior. } }
Java
@Override public void onPause() { // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode()) { // Continue playback. ... } else { // Use existing playback logic for paused activity behavior. ... } }
Gdy aktywność przejdzie z trybu PiP z powrotem do trybu pełnoekranowego, system wznowi aktywność i wywoła metodę onResume()
.
Używanie jednej aktywności odtwarzania w przypadku PiP
W Twojej aplikacji użytkownik może wybrać nowy film podczas przeglądania treści na głównym ekranie, gdy odtwarzanie filmu odbywa się w trybie obrazu w obrazie. odtwarzać nowy film w ramach dotychczasowej aktywności odtwarzania w trybie pełnoekranowym, zamiast uruchamiać nową aktywność, która może zdezorientować użytkownika;
Aby zapewnić, że do obsługi żądań odtwarzania filmów używana jest jedna aktywność, a w razie potrzeby przełączana jest w tryb PiP lub z niego wyjmowana, ustaw w pliku manifestu android:launchMode
na singleTask
:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
W swojej aktywności zastąpij onNewIntent()
i obsługuj nowy film, w razie potrzeby przerywając odtwarzanie dotychczasowego.
Sprawdzone metody
Tryb obrazu w obrazie może być wyłączony na urządzeniach z niewielką ilością pamięci RAM. Zanim aplikacja zacznie korzystać z trybu PiP, sprawdź, czy jest on dostępny, wywołując funkcję hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
Tryb PiP jest przeznaczony do czynności, które wymagają odtwarzania filmów w trybie pełnoekranowym. Podczas przełączania aktywności do trybu PiP nie pokazuj niczego poza treścią wideo. Śledź, kiedy Twoja aktywność przechodzi w tryb PiP, i ukrywanie elementów interfejsu zgodnie z opisem w sekcji Zarządzanie interfejsem w trybie PiP.
Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie jest zaznaczona. Aby otrzymywać zdarzenia wejścia w trybie PiP, użyj elementu MediaSession.setCallback()
.
Więcej informacji o używaniu setCallback()
znajdziesz w artykule Wyświetlanie karty Obecnie odtwarzane.
Gdy aplikacja jest w trybie PiP, odtwarzanie filmu w oknie PiP może powodować zakłócenia dźwięku w innej aplikacji, np. w odtwarzaczu muzycznym lub aplikacji do wyszukiwania głosowego. Aby tego uniknąć, poproś o uzyskanie kontroli nad dźwiękiem podczas odtwarzania filmu i obserwuj powiadomienia o zmianie kontroli nad dźwiękiem zgodnie z opisem w artykule Zarządzanie kontrolą nad dźwiękiem. Jeśli w trybie PiP otrzymasz powiadomienie o utracie skupienia dźwięku, wstrzymaj lub zatrzymaj odtwarzanie filmu.
Gdy aplikacja ma przejść do trybu PiP, pamiętaj, że tylko aktywne okno będzie wyświetlane w trybie obrazu w obrazie. W niektórych sytuacjach, np. na urządzeniach z wieloma oknami, możliwe jest, że aktywność poniżej będzie widoczna obok aktywności PiP. W takim przypadku należy odpowiednio zająć się sprawą, w tym onResume()
lub onPause()
. Możliwe też, że użytkownik wejdzie w interakcję z tą aktywnością. Jeśli np. wyświetlana jest aktywność listy filmów, a w tle odtwarzany jest film w trybie obrazu w obrazie, użytkownik może wybrać nowy film z listy, a aktywność obrazu w obrazie powinna się odpowiednio zaktualizować.
Dodatkowy przykładowy kod
Aby pobrać przykładową aplikację napisaną w języku Kotlin, zapoznaj się z artykułem Przykładowa aplikacja Android PictureInPicture (Kotlin).