Począwszy od Androida 8.0 (interfejs API na poziomie 26), Android umożliwia uruchamianie aktywności w trybie obrazu w obrazie (PiP). PiP to specjalny rodzaj trybu wielu okien, który jest najczęściej używany 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 obrazu w obrazie korzysta z interfejsów API obsługujących wiele okien, które są dostępne w Androidzie 7.0, aby wyświetlać przypięte okno nakładki wideo. Aby dodać do aplikacji tryb obrazu w obrazie, musisz zarejestrować działania, które go obsługują, w razie potrzeby przełączyć działanie w tryb obrazu w obrazie i upewnić się, że elementy interfejsu są ukryte, a odtwarzanie wideo jest kontynuowane, gdy działanie jest w trybie obrazu w obrazie.
Okno PiP pojawi się w górnej warstwie ekranu, w rogu wybranym przez system.
Tryb obrazu w obrazie jest też obsługiwany na zgodnych urządzeniach z Androidem TV OS z Androidem 14 (API na poziomie 34) lub nowszym. Chociaż istnieje wiele podobieństw, podczas korzystania z obrazu w obrazie na telewizorze należy wziąć pod uwagę dodatkowe kwestie.
Jak użytkownicy mogą wchodzić w interakcję z oknem PiP
Użytkownicy mogą przeciągnąć okno PiP w inne miejsce. Od Androida 12 użytkownicy mogą też:
Kliknij okno, aby wyświetlić przełącznik pełnego ekranu, przycisk zamykania, przycisk ustawień i niestandardowe działania udostępniane przez aplikację (np. elementy sterujące odtwarzaniem).
Kliknij dwukrotnie okno, aby przełączać się między bieżącym rozmiarem obrazu w obrazie a maksymalnym lub minimalnym rozmiarem obrazu w obrazie. Na przykład kliknięcie dwukrotnie zmaksymalizowanego okna spowoduje jego zminimalizowanie i odwrotnie.
Schowaj okno, przeciągając je do lewej lub prawej krawędzi. Aby przywrócić okno, kliknij widoczną część schowanego okna lub przeciągnij je.
Zmień rozmiar okna PIP, używając gestu uszczypnięcia.
Aplikacja decyduje, kiedy bieżąca aktywność przechodzi w tryb obrazu w obrazie. Oto kilka przykładów:
Aktywność może przejść w tryb obrazu w obrazie, gdy użytkownik naciśnie przycisk ekranu głównego lub przesunie palcem w górę do ekranu głównego. Dzięki temu Mapy Google mogą nadal wyświetlać wskazówki, gdy użytkownik wykonuje w tym samym czasie inne czynności.
Aplikacja może przenieść film do trybu obraz w obrazie, gdy użytkownik wróci z filmu, aby przeglądać inne treści.
Aplikacja może przełączyć film w tryb obrazu w obrazie, gdy użytkownik ogląda końcówkę odcinka. Na ekranie głównym wyświetlają się informacje promocyjne lub podsumowujące dotyczące kolejnego odcinka serialu.
Aplikacja może umożliwiać użytkownikom dodawanie do kolejki dodatkowych treści podczas oglądania filmu. Film jest nadal odtwarzany w trybie obrazu w obrazie, a na ekranie głównym wyświetla się aktywność wyboru treści.
Deklarowanie obsługi trybu obraz w obrazie
Domyślnie system nie obsługuje automatycznie trybu obrazu w obrazie w przypadku aplikacji. Jeśli chcesz, aby Twoja aplikacja obsługiwała tryb obrazu w obrazie, zarejestruj aktywność związaną z wideo w pliku manifestu, ustawiając wartość android:supportsPictureInPicture
na true
. Określ też, że aktywność obsługuje zmiany konfiguracji układu, aby nie była ponownie uruchamiana, gdy podczas przejść do trybu obrazu w obrazie nastąpią zmiany układu.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Przełączanie aktywności na tryb obrazu w obrazie
Od Androida 12 możesz przełączyć aktywność w tryb obrazu w obrazie, ustawiając flagę setAutoEnterEnabled
na true
. Dzięki temu ustawieniu aktywność automatycznie przełącza się w tryb obrazu w obrazie w razie potrzeby bez konieczności jawnego wywoływania enterPictureInPictureMode()
w onUserLeaveHint
. Ma to dodatkową zaletę w postaci płynniejszych przejść. Więcej informacji znajdziesz w artykule Płynniejsze przechodzenie do trybu obrazu w obrazie podczas nawigacji gestami.
Jeśli kierujesz reklamy na Androida 11 lub starszego, aktywność musi wywołać
enterPictureInPictureMode()
aby przejść do trybu obrazu w obrazie. Na przykład poniższy kod przełącza aktywność w tryb obrazu w obrazie, gdy użytkownik kliknie w interfejsie aplikacji specjalny przycisk:
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 uwzględnić logikę, która przełącza aktywność w tryb obrazu w obrazie zamiast przenosić ją w tle. Na przykład Mapy Google przełączają się w tryb obrazu w obrazie, gdy użytkownik naciśnie przycisk Początek lub Ostatnie podczas nawigacji w aplikacji. Możesz obsłużyć ten przypadek, 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 obrazu w obrazie
W Androidzie 12 wprowadzono znaczące zmiany wizualne w animowanych przejściach między oknami pełnoekranowymi a oknami w trybie obraz w obrazie. Zdecydowanie zalecamy wprowadzenie wszystkich odpowiednich zmian. Po ich wprowadzeniu będą one automatycznie skalowane na większych ekranach, takich jak urządzenia składane i tablety, bez konieczności wykonywania dodatkowych czynności.
Jeśli Twoja aplikacja nie zawiera odpowiednich aktualizacji, przejścia do trybu „obraz w obrazie” nadal będą działać, ale animacje będą mniej dopracowane. Na przykład przejście z trybu pełnoekranowego do trybu obrazu w obrazie może spowodować zniknięcie okna obrazu w obrazie podczas przejścia, zanim pojawi się ponownie po zakończeniu przejścia.
Zmiany te obejmują:
- Płynniejsze przechodzenie do trybu obrazu w obrazie podczas korzystania z nawigacji przy użyciu gestów
- Ustawienie odpowiedniego
sourceRectHint
do włączania i wyłączania trybu obrazu w obrazie - Wyłączanie płynnej zmiany rozmiaru w przypadku treści innych niż filmy
Aby uzyskać więcej informacji o włączaniu płynnego przejścia, zapoznaj się z przykładem Kotlin PictureInPicture na Androidzie.
Płynniejsze przechodzenie do trybu obrazu w obrazie podczas nawigacji przy użyciu gestów
Od Androida 12 flaga setAutoEnterEnabled
zapewnia znacznie płynniejszą animację podczas przechodzenia do treści wideo w trybie obrazu w obrazie za pomocą nawigacji gestami, np. podczas przesuwania palcem w górę do ekranu głównego z trybu pełnoekranowego.
Aby wprowadzić tę zmianę, wykonaj te czynności. W celu uzyskania dodatkowych informacji zapoznaj się z tym przykładem:
Użyj
setAutoEnterEnabled
, aby utworzyćPictureInPictureParams.Builder
:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Zadzwoń do
setPictureInPictureParams
z aktualnymiPictureInPictureParams
informacjami. Aplikacja nie czeka na wywołanie zwrotneonUserLeaveHint
(jak w Androidzie 11).Możesz na przykład wywołać funkcję
setPictureInPictureParams
przy pierwszym odtworzeniu i przy każdym kolejnym, jeśli zmieni się format obrazu.Wywołuj funkcję
setAutoEnterEnabled(false)
tylko wtedy, gdy jest to konieczne. Na przykład nie chcesz włączać trybu PIP, jeśli odtwarzanie jest wstrzymane.
Ustaw odpowiedni sourceRectHint
do włączania i wyłączania trybu obrazu w obrazie
Od wprowadzenia funkcji Obraz w obrazie w Androidzie 8.0 parametr setSourceRectHint
wskazywał obszar aktywności widoczny po przejściu do trybu obraz w obrazie, np. granice widoku filmu w odtwarzaczu wideo.
W Androidzie 12 system używa sourceRectHint
, aby wdrożyć znacznie płynniejszą animację zarówno podczas włączania, jak i wyłączania trybu obraz w obrazie.
Aby prawidłowo ustawić sourceRectHint
do włączania i wyłączania trybu obrazu w obrazie:
Skonstruuj
PictureInPictureParams
, używając odpowiednich granic jakosourceRectHint
. Zalecamy też dołączenie do odtwarzacza wideo detektora zmian 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
, zanim system rozpocznie przejście wyjścia. Gdy system ma wyjść z trybu obrazu w obrazie, hierarchia widoków aktywności jest układana w konfiguracji docelowej (np. na pełnym ekranie). Aplikacja może dołączyć detektor zmiany układu do widoku głównego lub widoku docelowego (np. widoku odtwarzacza wideo), aby wykrywać zdarzenie i aktualizować wartość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łynnej zmiany rozmiaru w przypadku treści innych niż filmy
Android 12 dodaje flagę setSeamlessResizeEnabled
, która zapewnia znacznie płynniejszą animację przenikania podczas zmiany rozmiaru treści innych niż wideo w oknie obrazu w obrazie. Wcześniej zmiana rozmiaru treści innych niż wideo w oknie obrazu w obrazie mogła powodować nieprzyjemne artefakty wizualne.
Aby włączyć płynną zmianę rozmiaru treści wideo:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());
Obsługa interfejsu podczas korzystania z trybu obrazu w obrazie
Gdy aktywność wchodzi w tryb obraz w obrazie lub z niego wychodzi, system wywołuje metodę Activity.onPictureInPictureModeChanged()
lub Fragment.onPictureInPictureModeChanged()
.
W Androidzie 15 wprowadzono zmiany, które zapewniają jeszcze płynniejsze przejście do trybu obrazu w obrazie. Jest to przydatne w przypadku aplikacji, które mają elementy interfejsu użytkownika nakładane na główny interfejs użytkownika, który przechodzi w tryb obrazu w obrazie.
Deweloperzy używają wywołania zwrotnego onPictureInPictureModeChanged()
do definiowania logiki, która przełącza widoczność nakładanych elementów interfejsu.
To wywołanie zwrotne jest wywoływane po zakończeniu animacji wejścia lub wyjścia z trybu obrazu w obrazie.
Od Androida 15 klasa PictureInPictureUiState
zawiera nowy stan.
W tym nowym stanie interfejsu aplikacje kierowane na Androida 15 obserwują wywołanie wywołania zwrotnego Activity#onPictureInPictureUiStateChanged()
z wartością isTransitioningToPip()
natychmiast po rozpoczęciu animacji PiP.
Wiele elementów interfejsu nie jest istotnych dla aplikacji w trybie obrazu w obrazie. Dotyczy to na przykład widoków lub układów zawierających informacje takie jak sugestie, nadchodzące filmy, oceny i tytuły. Gdy aplikacja przejdzie w tryb obrazu w obrazie, użyj wywołania zwrotnego onPictureInPictureUiStateChanged()
, aby ukryć te elementy interfejsu. Gdy aplikacja przechodzi do trybu pełnoekranowego z okienka obrazu w obrazie, użyj wywołania zwrotnego onPictureInPictureModeChanged()
, aby odkryć te elementy, jak pokazano w przykładach poniżej:
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 obrazu w obrazie) pomaga zapewnić płynniejszą animację wejścia w tryb obrazu w obrazie bez migotania.
Zastąp te wywołania zwrotne, aby ponownie narysować elementy interfejsu 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ć trudne do zobaczenia. Odtwarzanie filmów z minimalnym interfejsem zapewnia najlepszą wygodę.
Jeśli Twoja aplikacja musi udostępniać niestandardowe działania w trybie obrazu w obrazie, zapoznaj się z sekcją Dodawanie elementów sterujących na tej stronie. Usuń inne elementy interfejsu przed przejściem działania w tryb obrazu w obrazie i przywróć je, gdy działanie ponownie zajmie cały ekran.
Dodaj elementy sterujące
Gdy użytkownik otworzy menu okna (dotykając okna na urządzeniu mobilnym lub wybierając menu na pilocie telewizora), mogą się w nim pojawić elementy sterujące.
Jeśli aplikacja ma aktywną sesję multimedialną, pojawią się elementy sterujące odtwarzaniem, wstrzymywaniem, następnym i poprzednim.
Możesz też jawnie określić działania niestandardowe, tworząc
PictureInPictureParams
za pomocą
PictureInPictureParams.Builder.setActions()
przed przejściem do trybu obrazu w obrazie i przekazując parametry podczas przechodzenia do tego trybu za pomocą
enterPictureInPictureMode(android.app.PictureInPictureParams)
lub
setPictureInPictureParams(android.app.PictureInPictureParams)
.
Zachowaj ostrożność. Jeśli spróbujesz dodać więcej niż getMaxNumPictureInPictureActions()
, otrzymasz tylko maksymalną liczbę.
Kontynuowanie odtwarzania filmu w trybie obraz w obrazie
Gdy aktywność przełączy się na tryb obrazu w obrazie, system umieści ją w stanie wstrzymania i wywoła metodę onPause()
aktywności. Odtwarzanie wideo nie powinno być wstrzymywane, ale powinno być kontynuowane, jeśli aktywność zostanie wstrzymana podczas przechodzenia do trybu obrazu w obrazie.
W Androidzie 7.0 i nowszych należy wstrzymywać i wznawiać odtwarzanie wideo, gdy system wywołuje metody aktywności onStop()
i 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 jest ustawiona na true
, a musisz wstrzymać odtwarzanie w implementacji onPause()
, sprawdź tryb obrazu w obrazie, wywołując isInPictureInPictureMode()
, i odpowiednio obsłuż odtwarzanie. Na 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ść przełączy się z trybu obrazu w obrazie z powrotem na tryb pełnoekranowy, system wznowi aktywność i wywoła metodę onResume()
.
Używanie jednej aktywności odtwarzania w trybie obraz w obrazie
W aplikacji użytkownik może wybrać nowy film podczas przeglądania treści na ekranie głównym, gdy aktywność odtwarzania filmu jest w trybie obrazu w obrazie. Odtwórz nowy film w ramach istniejącej aktywności odtwarzania w trybie pełnoekranowym, zamiast uruchamiać nową aktywność, która może zmylić użytkownika.
Aby mieć pewność, że do żądań odtwarzania wideo jest używana jedna aktywność, która w razie potrzeby przełącza się w tryb obrazu w obrazie i z niego wychodzi, ustaw w pliku manifestu atrybut android:launchMode
aktywności na singleTask
:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
W aktywności zastąp
onNewIntent()
i obsłuż nowy film, w razie potrzeby zatrzymując odtwarzanie bieżącego filmu.
Sprawdzone metody
Funkcja obrazu w obrazie może być wyłączona na urządzeniach z niewielką ilością pamięci RAM. Zanim aplikacja zacznie korzystać z trybu obrazu w obrazie, sprawdź, czy jest on dostępny, wywołując funkcję hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
Tryb obrazu w obrazie jest przeznaczony do aktywności, które odtwarzają wideo na pełnym ekranie. Podczas przełączania aktywności w tryb obrazu w obrazie unikaj wyświetlania czegokolwiek poza treściami wideo. Śledź, kiedy aktywność przechodzi w tryb obrazu w obrazie, i ukrywaj elementy interfejsu zgodnie z opisem w sekcji Obsługa interfejsu podczas korzystania z trybu obrazu w obrazie.
Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie otrzymuje fokusu wejściowego. Aby otrzymywać zdarzenia wejściowe w trybie obrazu w obrazie, użyj MediaSession.setCallback()
.
Więcej informacji o używaniu setCallback()
znajdziesz w artykule Wyświetlanie karty „Teraz odtwarzane”.
Gdy aplikacja jest w trybie obraz w obrazie, odtwarzanie wideo w oknie obrazu w obrazie może powodować zakłócenia dźwięku w innej aplikacji, np. w odtwarzaczu muzyki lub aplikacji do wyszukiwania głosowego. Aby tego uniknąć, poproś o skupienie dźwięku, gdy zaczniesz odtwarzać film, i obsługuj powiadomienia o zmianie skupienia dźwięku zgodnie z opisem w artykule Zarządzanie skupieniem dźwięku. Jeśli w trybie obrazu w obrazie otrzymasz powiadomienie o utracie fokusu dźwięku, wstrzymaj lub zatrzymaj odtwarzanie filmu.
Gdy aplikacja ma przejść do trybu obrazu w obrazie, tylko aktywność na górze przechodzi do tego trybu. W niektórych sytuacjach, np. na urządzeniach z wieloma oknami, aktywność poniżej może być teraz widoczna i ponownie wyświetlana obok aktywności w trybie obraz w obrazie. W takim przypadku należy podjąć odpowiednie działania, w tym wywołać funkcję zwrotną onResume()
lub onPause()
. Możliwe jest też, że użytkownik wejdzie w interakcję z aktywnością. Jeśli na przykład wyświetlasz aktywność listy filmów i aktywność odtwarzania filmu w trybie obraz w obrazie, użytkownik może wybrać nowy film z listy, a aktywność w trybie obraz w obrazie powinna się odpowiednio zaktualizować.
Dodatkowy przykładowy kod
Aby pobrać przykładową aplikację napisaną w języku Kotlin, zobacz Android PictureInPicture Sample (Kotlin).