Od Androida 8.0 (poziom API 26) aktywności mogą być uruchamiane w trybie obraz w obrazie. PiP to specjalny rodzaj trybu wielu okien, który jest najczęściej używany do odtwarzania filmów. Umożliwia użytkownikowi oglądanie filmu w małym oknie przypiętym do rogu ekranu, a jednocześnie przełączanie innych aplikacji lub wyszukiwanie 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ę na wierzchniej 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 dwukrotne zmaksymalizowanego okna spowoduje jego zminimalizowanie i odwrotnie.
Ukryj okno, przeciągając je do lewej lub prawej krawędzi. Aby wycofać okno z ukrycia, kliknij widoczną część ukrytego okna lub przeciągnij ją.
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 koniec odcinka. Na ekranie głównym wyświetlają się informacje promocyjne lub podsumowujące dotyczące następnego odcinka serialu.
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 będzie wyświetlana 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 Twoja 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. Dodatkową korzyścią jest płynniejsze przechodzenie między scenami. 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ływać
enterPictureInPictureMode()
aby przełączyć się na tryb 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 wprowadziliśmy 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 zostaną one automatycznie dostosowane do dużych ekranów, 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
sourceRectHintdo 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 korzystania z 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
setPictureInPictureParamsz aktualnym numeremPictureInPictureParamsz wyprzedzeniem. Aplikacja nie czeka na wywołanie zwrotneonUserLeaveHint(jak w Androidzie 11).Możesz na przykład wywołać funkcję
setPictureInPictureParamsprzy pierwszym odtworzeniu i przy każdym kolejnym odtworzeniu, 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 obrazu w obrazie, np. granice widoku filmu w odtwarzaczu wideo.
W Androidzie 12 system używa sourceRectHint, aby zapewnić 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 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, zanim system rozpocznie przejście do stanu 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śćsourceRectHintprzed 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 zdefiniowania 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 obrazu w obrazie.
Wiele elementów interfejsu nie jest istotnych dla aplikacji w trybie obrazu w obrazie, np. widoki lub układy zawierające 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 do trybu 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 wejściem w tryb obrazu w obrazie i przekazując parametry podczas włączania 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ść przechodzi w tryb obrazu w obrazie, system umieszcza ją w stanie wstrzymania i wywołuje 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 nie ustawisz flagi setAutoEnterEnabled na true i 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ść przejdzie z trybu obrazu w obrazie z powrotem do trybu pełnoekranowego, 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 metodę 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 ekranu 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 pojawić się 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(). Użytkownik może też wchodzić w interakcje 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) (w języku angielskim).