Począwszy od Androida 8.0 (poziom interfejsu API 26) Android zezwala na uruchamianie działań w trybie obrazu 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.
Obraz w obrazie jest też obsługiwany na zgodnych urządzeniach z systemem operacyjnym Android TV i z systemem Android 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ą korzystać z okna obrazu w obrazie
Użytkownicy mogą przeciągać okno PiP w inne miejsce. Od Androida 12 użytkownicy mogą też:
Kliknij okno, aby wyświetlić przełącznik trybu pełnoekranowego, przycisk Zamknij, przycisk ustawień i 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 lub minimalnym rozmiarem obrazu w obrazie. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna spowoduje jego zminimalizowanie, a odwrotnie.
Ukryj okno, przeciągając je do lewej lub prawej krawędzi. Aby odsłonić okno, kliknij widoczną część ukrytego okna lub przeciągnij je.
Zmień rozmiar okna PiP, używając funkcji powiększania.
Aplikacja określa, 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 na 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 obraz w obrazie, gdy użytkownik wróci z filmu, aby przejrzeć 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, aby Twoja aplikacja obsługiwała tryb obrazu w pliku, zarejestruj aktywność związaną z wideo w pliku manifestu, ustawiając wartość android:supportsPictureInPicture
na true
. Określ też, że działanie ma obsługiwać zmiany konfiguracji układu, tak aby aktywność nie była ponownie uruchamiana po zmianie układu w trakcie przechodzenia w trybie PIP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Przełączanie aktywności do trybu obrazu w obrębie obrazu
Od Androida 12 możesz przełączyć aktywność do trybu obrazu w obrazie, ustawiając flagę setAutoEnterEnabled
na true
. Gdy to ustawienie jest włączone, aktywność automatycznie przełącza się w razie potrzeby w tryb PIP bez konieczności jawnego wywoływania funkcji enterPictureInPictureMode()
w onUserLeaveHint
. Dodatkową zaletą jest znacznie płynniejsze przechodzenie między kartami. 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 przejść do trybu obrazu w obrazie. Na przykład poniższy 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 przechwycić ten przypadek, zastępując fragment 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
- Ustawiam właściwą wartość
sourceRectHint
umożliwiającą włączanie i wyłączanie trybu obraz 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.
Płynniejsze przejście do trybu PIP z nawigacji przy użyciu gestów
Począwszy od Androida 12 flaga setAutoEnterEnabled
zapewnia znacznie płynniejsze animacje podczas przechodzenia do treści wideo w trybie obraz w obrazie za pomocą nawigacji gestami, na przykład podczas przesuwania w górę z trybu pełnoekranowego do ekranu głównego.
Wykonaj poniższe czynności, aby wprowadzić tę zmianę, i zapoznaj się z tym przykładem:
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łanie zwrotneonUserLeaveHint
(tak 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ń pod numer
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
Począwszy od wprowadzenia funkcji PIP w Androidzie 8.0 na Androidzie 8.0 setSourceRectHint
określał obszar aktywności widoczny po przejściu w tryb obraz w obrazie – na przykład granice wyświetlenia 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 włączania i wyłączania trybu obraz w obrazie:
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) obiektsourceRectHint
, który będzie wykrywać zdarzenia i aktualizować go 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 innych niż wideo w oknie PiP. Wcześniej zmiana rozmiaru treści innych niż wideo w oknie PIP mogła powodować tworzenie atrakcyjnych wizualnie artefaktów.
Aby wyłączyć płynną zmianę rozmiaru w przypadku treści innych niż wideo:
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 obrazu w obrazie lub go opuszcza, 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 przydatne w przypadku aplikacji, których elementy interfejsu są nałożone na główny interfejs, który przechodzi do obrazu w obrazie.
Deweloperzy używają wywołania zwrotnego onPictureInPictureModeChanged()
do zdefiniowania logiki przełączającej widoczność nakładanych elementów interfejsu.
Ten wywołanie zwrotne jest wywoływane po zakończeniu animacji otwierania lub zamykania PiP.
W Androidzie 15 klasa PictureInPictureUiState
otrzymała 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.
Jest wiele elementów interfejsu, które nie mają znaczenia w aplikacji w trybie obrazu w obrazie. Są to na przykład wyświetlenia lub układ zawierające takie informacje 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 interakcję z elementami interfejsu aplikacji, gdy jest ona w trybie obrazu w obrazie, a szczegóły małych elementów interfejsu mogą być słabo widoczne. 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
W oknie funkcji obraz w obrazie mogą być widoczne elementy sterujące, gdy użytkownik otworzy menu okna (klikając okno na urządzeniu mobilnym lub wybierając menu z 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, a zamiast tego powinno być kontynuowane, jeśli aktywność została wstrzymana podczas przejścia do trybu PiP.
W Androidzie 7.0 i nowszych należy wstrzymać i wznowić odtwarzanie filmu, gdy system wywoła onStop()
i onStart()
dotyczące Twojej aktywności. 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. 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ść przejdzie z trybu PiP 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 ekranie głównym, podczas gdy odtwarzanie filmu jest włączone w trybie PIP. Odtwarzaj nowy film w ramach dotychczasowej aktywności w trybie pełnoekranowym, zamiast rozpoczynać nowe działanie, które 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 Twoja aplikacja zacznie używać funkcji PIP, sprawdź, czy jest ona dostępna, wywołując metodę hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
Obraz w obrazie jest przeznaczony do działań, w których odtwarzane są filmy na pełnym ekranie. Podczas przełączania aktywności do trybu PiP nie pokazuj niczego poza treścią wideo. Śledź, kiedy Twoja aktywność przechodzi w tryb PiP, i ukrywaj elementy 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 obraz w obrazie, odtwarzanie filmu w tym oknie może powodować zakłócenia dźwięku w innej aplikacji, np. w aplikacji odtwarzacza muzyki lub aplikacji do wyszukiwania głosowego. Aby tego uniknąć, poproś o włączenie funkcji audio po rozpoczęciu odtwarzania filmu i obsługuj powiadomienia o zmianie fokusu, jak opisano w sekcji Zarządzanie ostrością dźwięku. Jeśli otrzymasz powiadomienie o utracie ostrości dźwięku w trybie obraz w obrazie, wstrzymaj lub zatrzymaj odtwarzanie filmu.
Gdy aplikacja ma przejść do trybu PiP, zwróć uwagę, że tylko aktywna czynność jest przenoszona do 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 PiP. W takim przypadku należy odpowiednio zająć się tym zgłoszeniem, w tym onResume()
lub onPause()
. Możliwe też, że użytkownik wejdzie w interakcję z treścią. Jeśli na przykład widzisz aktywność związaną z listą filmów, a odtwarzanie filmu w trybie obrazu w obrazie, użytkownik może wybrać nowy film z listy i treść aktywności obrazu w obrazie powinna być aktualizowana.
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).