Od Androida 8.0 (poziom API 26) Android umożliwia uruchamianie aktywności w trybie obraz w obrazie (PiP). PIP to specjalny tryb wielu okien używany do odtwarzania filmów, rozmów wideo i nawigacji. Umożliwia użytkownikowi przypięcie okna bieżącej aktywności do rogu ekranu podczas przełączania się między aplikacjami lub przeglądania treści na ekranie głównym.
PiP korzysta z interfejsów API wielookienkowych udostępnionych w Androidzie 7.0, aby zapewnić przypięte okno nakładki wideo. Aby dodać PiP do aplikacji, musisz zarejestrować aktywności, które obsługują PiP, w razie potrzeby przełączyć aktywność w tryb PiP oraz upewnić się, że elementy interfejsu są ukryte, a odtwarzanie wideo jest kontynuowane, gdy aktywność jest w trybie PiP.
Okno PIP pojawia się w najwyższej warstwie ekranu, w rogu wybranym przez system.
PiP jest też obsługiwany na zgodnych urządzeniach z Androidem TV OS z Androidem 14 (poziom API 34) lub nowszym. Chociaż istnieje wiele podobieństw, istnieją dodatkowe kwestie, które należy wziąć pod uwagę podczas korzystania z PiP na telewizorze.
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ż:
Dotknąć okna, aby wyświetlić przełącznik pełnego ekranu, przycisk zamykania, przycisk ustawień i działania niestandardowe udostępniane przez aplikację (np. elementy sterujące odtwarzaniem).
Dotknąć okna dwukrotnie, aby przełączać się między bieżącym rozmiarem PiP a maksymalnym lub minimalnym rozmiarem PiP. Na przykład dwukrotne dotknięcie zmaksymalizowanego okna powoduje jego zminimalizowanie i odwrotnie.
Ukryć okno, przeciągając je do lewej lub prawej krawędzi. Aby przywrócić okno, dotknij widocznej części ukrytego okna lub przeciągnij je.
Zmienić rozmiar okna PiP za pomocą gestu uszczypnięcia.
Aplikacja kontroluje, kiedy bieżąca aktywność przechodzi w tryb PiP. Oto przykłady:
Aktywność może przejść w tryb PiP, gdy użytkownik dotknie przycisku ekranu głównego lub przesunie palcem w górę do ekranu głównego. W ten sposób Mapy Google nadal wyświetlają wskazówki, gdy użytkownik jednocześnie wykonuje inną aktywność.
Aplikacja może przenieść film do trybu PiP, gdy użytkownik wróci z filmu, aby przeglądać inne treści.
Aplikacja może przełączyć film w tryb PiP, 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 jest nadal odtwarzany w trybie PIP, a na ekranie głównym wyświetla się aktywność wyboru treści.
Deklarowanie obsługi PiP
Domyślnie system nie obsługuje automatycznie PiP w przypadku aplikacji. Jeśli chcesz, aby Twoja aplikacja obsługiwała PiP, zarejestruj aktywność 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ść w tryb PiP nastąpią zmiany układu.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Wdrażanie obrazu w obrazie za pomocą Jetpack
Użyj biblioteki Jetpack Picture-in-Picture, aby wdrożyć funkcję obrazu w obrazie. Ułatwia ona integrację i ogranicza typowe problemy w aplikacji. Przykład użycia znajdziesz w naszej przykładowej aplikacji na platformę. Jeśli jednak wolisz zaimplementować tryb obrazu w obrazie za pomocą interfejsów API platformy, zapoznaj się z tą dokumentacją.
Przełączanie aktywności na PiP
Od Androida 12 możesz przełączyć aktywność w tryb PiP, ustawiając
flagę setAutoEnterEnabled na true. Dzięki temu ustawieniu aktywność
automatycznie przechodzi w tryb PiP w razie potrzeby bez konieczności jawnego wywoływania
enterPictureInPictureMode() w onUserLeaveHint. Ma to dodatkową zaletę, ponieważ zapewnia znacznie płynniejsze przejścia. Więcej informacji znajdziesz w artykule Płynniejsze przejścia do trybu PIP podczas nawigacji przy użyciu gestów.
Jeśli kierujesz aplikację na Androida 11 lub starszego, aktywność musi wywołać
enterPictureInPictureMode()
aby przejść w tryb PiP. 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 uwzględnić logikę, która przełącza aktywność w tryb PiP zamiast przechodzić w tło. Na przykład Mapy Google przechodzą w tryb PiP, jeśli użytkownik naciśnie przycisk ekranu głównego lub ostatnio używanych aplikacji podczas nawigacji w aplikacji. Możesz
przechwycić 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ścia do trybu PiP
W Androidzie 12 wprowadziliśmy znaczące ulepszenia wizualne animowanych przejść między oknami pełnoekranowymi a oknami PiP. Zdecydowanie zalecamy wprowadzenie wszystkich odpowiednich zmian. Po ich wprowadzeniu zmiany te automatycznie dostosowują się 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 PiP nadal działają, ale animacje są mniej dopracowane. Na przykład przejście z trybu pełnoekranowego do trybu PiP może spowodować zniknięcie okna PiP podczas przejścia, zanim pojawi się ponownie po jego zakończeniu.
Te zmiany obejmują:
- Płynniejsze przejścia do trybu PIP podczas nawigacji przy użyciu gestów
- Ustawianie odpowiedniego parametru
sourceRectHintdo wchodzenia i wychodzenia z trybu PiP - Wyłączanie płynnej zmiany rozmiaru w przypadku treści innych niż wideo
Aby włączyć płynne przejścia, zapoznaj się z przykładem Android Kotlin PictureInPicture.
Płynniejsze przejścia do trybu PIP 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 PiP za pomocą nawigacji gestami
– na przykład podczas przesuwania palcem w górę do ekranu głównego z trybu pełnoekranowego.
Aby wprowadzić tę zmianę:
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());
Wcześniej wywołaj
setPictureInPictureParamsz aktualnymiPictureInPictureParams. Aplikacja nie czeka na wywołanie zwrotneonUserLeaveHint(jak to miało miejsce w Androidzie 11).Na przykład możesz wywołać
setPictureInPictureParamsprzy pierwszym odtworzeniu i każdym kolejnym odtworzeniu, jeśli zmieni się współczynnik proporcji.Wywołaj
setAutoEnterEnabled(false), ale tylko wtedy, gdy jest to konieczne. Na przykład prawdopodobnie nie chcesz przechodzić w tryb PiP, jeśli bieżące odtwarzanie jest wstrzymane.
Ustawianie odpowiedniego parametru sourceRectHint do wchodzenia i wychodzenia z trybu PiP
Od wprowadzenia PiP w Androidzie 8.0, setSourceRectHint
wskazuje obszar aktywności, który jest widoczny po przejściu w tryb
obraz w obrazie – na przykład granice widoku wideo w odtwarzaczu wideo.
W Androidzie 12 system używa sourceRectHint do implementowania znacznie płynniejszej animacji zarówno podczas wchodzenia, jak i wychodzenia z trybu PiP.
Aby prawidłowo ustawić sourceRectHint do wchodzenia i wychodzenia z trybu PiP:
Utwórz
PictureInPictureParamsużywając odpowiednich granic jakosourceRectHint. Zalecamy też dołączenie detektora zmian układu do odtwarzacza wideo: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 PiP, hierarchia widoków aktywności jest układana w konfiguracji docelowej (np. pełny ekran). Aplikacja może dołączyć detektor zmian układu do widoku głównego lub widoku docelowego (np. widoku odtwarzacza wideo), aby wykryć zdarzenie i zaktualizować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ż wideo
W Androidzie 12 dodaliśmy flagę setSeamlessResizeEnabled, która zapewnia znacznie
płynniejszą animację przenikania 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ć nieprzyjemne artefakty wizualne.
Aby włączyć płynną zmianę rozmiaru w przypadku treści wideo:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());
Obsługa interfejsu podczas PiP
Gdy aktywność wchodzi w tryb obraz w obrazie (PiP) lub z niego wychodzi, system wywołuje Activity.onPictureInPictureModeChanged()
lub Fragment.onPictureInPictureModeChanged().
W Androidzie 15 wprowadziliśmy zmiany, które zapewniają jeszcze płynniejsze przejście podczas wchodzenia w tryb PiP. Jest to korzystne w przypadku aplikacji, które mają elementy interfejsu nakładane na główny interfejs, który przechodzi w tryb PiP.
Programiści 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 wchodzenia lub wychodzenia z trybu PiP.
Od Androida 15 klasa PictureInPictureUiState zawiera nowy stan.
Dzięki temu nowemu stanowi interfejsu aplikacje kierowane na Androida 15 obserwują wywołanie zwrotne Activity#onPictureInPictureUiStateChanged()
wywoływane z isTransitioningToPip() zaraz po rozpoczęciu animacji PiP.
Istnieje wiele elementów interfejsu, które nie są istotne dla aplikacji, gdy jest ona w trybie PiP. Są to np. widoki lub układy zawierające informacje takie jak sugestie, nadchodzące filmy, oceny i tytuły. Gdy aplikacja przechodzi w tryb PiP, użyj wywołania zwrotnego onPictureInPictureUiStateChanged(), aby ukryć te elementy interfejsu. Gdy aplikacja przechodzi z okna PiP w tryb pełnoekranowy, użyj wywołania zwrotnego onPictureInPictureModeChanged(), aby odkryć 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. } }
To szybkie przełączanie widoczności nieistotnych elementów interfejsu (w przypadku okna PiP) pomaga zapewnić płynniejszą animację wchodzenia w tryb PiP bez migotania.
Zastąp te wywołania zwrotne, aby ponownie narysować elementy interfejsu aktywności. Pamiętaj, że w trybie PiP Twoja aktywność jest wyświetlana w małym oknie. Gdy aplikacja jest w trybie PiP, użytkownicy nie mogą wchodzić w interakcję z elementami interfejsu aplikacji, a szczegóły małych elementów interfejsu mogą być trudne do zobaczenia. Aktywności odtwarzania wideo z minimalnym interfejsem zapewniają najlepsze wrażenia użytkowników.
Jeśli Twoja aplikacja musi udostępniać działania niestandardowe w trybie PiP, przeczytaj sekcję Dodawanie elementów sterujących na tej stronie. Usuń inne elementy interfejsu, zanim aktywność przejdzie w tryb PiP, i przywróć je, gdy aktywność ponownie przejdzie w tryb pełnoekranowy.
Dodawanie elementów sterujących
Okno PiP może wyświetlać elementy sterujące, gdy użytkownik otworzy menu okna (dotykając okna na urządzeniu mobilnym lub wybierając menu na pilocie telewizora).
Możesz też jawnie określić działania niestandardowe, tworząc
PictureInPictureParams
za pomocą
PictureInPictureParams.Builder.setActions()
przed przejściem w tryb PiP, i przekazać parametry podczas wchodzenia w tryb PiP 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 wideo w trybie PiP
Gdy aktywność przechodzi w tryb PiP, system umieszcza ją w stanie wstrzymania
i wywołuje metodę aktywności
onPause(). Odtwarzanie wideo nie powinno być wstrzymywane, ale powinno być kontynuowane, jeśli aktywność jest wstrzymana podczas przechodzenia w tryb PiP.
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 PiP w onPause(), i jawnie kontynuować odtwarzania.
Jeśli nie ustawisz flagi setAutoEnterEnabled na true i musisz
wstrzymać odtwarzanie w implementacji onPause(), sprawdź tryb PiP, 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ść przechodzi z trybu PiP z powrotem w tryb pełnoekranowy, system
wznawia aktywność i wywołuje metodę
onResume().
Używanie jednej aktywności odtwarzania w trybie PiP
W aplikacji użytkownik może wybrać nowy film podczas przeglądania treści na ekranie głównym, gdy aktywność odtwarzania wideo jest w trybie PiP. Odtwórz nowy film w 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ść i że w razie potrzeby przechodzi ona w tryb PiP lub z niego wychodzi, ustaw w pliku manifestu 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.
Obsługa PiP w aplikacjach aparatu
Aby włączyć PiP w aplikacjach aparatu, musisz mieć pewność, że aparat pozostaje aktywny w trybie PiP, nie zamykając go po wywołaniu onPause():
Java
@Override
public void onPause() {
super.onPause();
// Don't close the camera if the app is entering PiP mode
if (!isInPictureInPictureMode()) {
closeCamera();
}
}
Podobnie jak w innych przypadkach użycia, ukryj nieistotne elementy interfejsu (np. elementy sterujące i nakładki) oraz dodaj działania niestandardowe, aby sterować aparatem (np. zatrzymać nagrywanie lub odwrócić aparat).
Obliczanie sourceRectHint na potrzeby płynnych przejść
Podanie dokładnego sourceRectHint z dokładnymi współrzędnymi ekranu wizjera aparatu jest niezbędne do płynnej animacji wchodzenia. Granice możesz uzyskać z widoku podglądu za pomocą getGlobalVisibleRect() w ten sposób:
Java
View previewView = findViewById(R.id.preview_view);
Rect globalRect = new Rect();
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect);
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setSourceRectHint(globalRect)
.build();
setPictureInPictureParams(params);
Sprawdzone metody
PiP może być wyłączony na urządzeniach z małą ilością pamięci RAM. Zanim aplikacja zacznie korzystać z PiP,
sprawdź, czy jest on dostępny, wywołując
hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).
PIP jest przeznaczony dla aktywności, które odtwarzają wideo pełnoekranowe. Podczas przełączania aktywności w tryb PiP unikaj wyświetlania czegokolwiek poza treściami wideo. Śledź, kiedy aktywność przechodzi w tryb PiP, i ukrywaj elementy interfejsu zgodnie z opisem w sekcji Obsługa interfejsu podczas PiP.
Gdy aktywność jest w trybie PiP, domyślnie nie otrzymuje fokusu wejścia. Aby
otrzymywać zdarzenia wejścia w trybie PiP, użyj
MediaSession.setCallback().
Więcej informacji o używaniu setCallback() znajdziesz w artykule Wyświetlanie karty „Co jest grane”
card.
Gdy aplikacja jest w trybie PiP, odtwarzanie wideo w oknie PiP 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 aktywność audio, gdy zaczniesz odtwarzać film, i obsługuj powiadomienia o zmianie aktywności audio zgodnie z opisem w sekcji Zarządzanie aktywnością audio. Jeśli w trybie PIP otrzymasz powiadomienie o utracie aktywności audio, wstrzymaj lub zatrzymaj odtwarzanie wideo.
Gdy aplikacja ma przejść w tryb PiP, pamiętaj, że tylko aktywność na wierzchu przechodzi w tryb obraz w obrazie. W niektórych sytuacjach, np. na urządzeniach wielookienkowych, aktywność poniżej może być teraz wyświetlana i ponownie widoczna obok aktywności PiP. Należy odpowiednio obsłużyć ten przypadek, w tym wywołanie zwrotne onResume() lub onPause() w aktywności poniżej. Możliwe jest też, że użytkownik będzie wchodzić w interakcję z aktywnością. Jeśli na przykład wyświetlasz aktywność listy filmów i aktywność odtwarzania wideo w trybie PiP, użytkownik może wybrać nowy film z listy, a aktywność PiP powinna się odpowiednio zaktualizować.
Dodatkowy przykładowy kod
Aby pobrać przykładową aplikację napisaną w Kotlinie, zobacz Android PictureInPicture Sample (Kotlin).