Od Androida 8.0 (poziom API 26) aktywności mogą być uruchamiane w trybie obraz w obrazie. PiP to specjalny rodzaj trybu 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 aplikacji lub przeglądania 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 najwyższej 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ągać 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 działania niestandardowe 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ść ma przejść 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.
Gdy użytkownik wróci z filmu, aby przeglądać inne treści, aplikacja może przenieść wideo do trybu obraz w obrazie.
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 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 obsługiwać tryb obrazu w obrazie w swojej aplikacji, zarejestruj aktywność związaną z filmem w manifeście, ustawiając 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"
...
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 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ą zaletą jest płynniejsze przechodzenie między stanami. Więcej informacji znajdziesz w artykule Płynniejsze przechodzenie do trybu PIP podczas nawigacji przy użyciu gestów.
Jeśli kierujesz aplikację na Androida 11 lub starszego, aktywność musi wywołać funkcję
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:
override fun onActionClicked(action: Action) {
if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
activity?.enterPictureInPictureMode()
return
}
}
Możesz uwzględnić logikę, która przełącza aktywność w tryb obrazu w obrazie zamiast przechodzenia w tło. Na przykład Mapy Google przełączają się w tryb obrazu w obrazie, gdy użytkownik naciśnie przycisk ekranu głównego lub ostatnio używanych aplikacji podczas nawigacji. Możesz przechwycić ten przypadek, zastępując funkcję onUserLeaveHint():
override fun 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 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 w trybie „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 PIP podczas przejścia, zanim pojawi się ponownie po jego zakończeniu.
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ć informacje o tym, jak zapewnić płynne przejście, zapoznaj się z przykładową aplikacją PictureInPicture w Kotlinie na Androida.
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:
- Użyj
setAutoEnterEnabled, aby utworzyćPictureInPictureParams.Builder:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build())
Note: When `setAutoEnterEnabled` is enabled, you don't need to explicitly
call [`enterPictureInPictureMode`][5] in [`onUserLeaveHint`][6].
Zadzwoń pod numer
setPictureInPictureParams, korzystając z aktualnych informacjiPictureInPictureParams, z 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, 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 obrazu w obrazie, 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 odbiornika zmiany układu:
val mOnLayoutChangeListener =
OnLayoutChangeListener { v: View?, left: Int,
top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop:
Int, oldRight: Int, oldBottom: Int ->
val sourceRectHint = Rect()
mYourVideoView.getGlobalVisibleRect(sourceRectHint)
val builder = 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 wykryć zdarzenie i zaktualizować wartośćsourceRectHintprzed rozpoczęciem animacji.
// 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()
)
}
}
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 PIP mogła powodować nieprzyjemne artefakty wizualne.
Aby włączyć płynną zmianę rozmiaru treści wideo:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(true)
.build())
Obsługa interfejsu podczas korzystania z trybu obrazu w obrazie
Gdy aktywność wchodzi w tryb obrazu w obrazie (PiP) lub z niego wychodzi, system wywołuje funkcję Activity.onPictureInPictureModeChanged() lub Fragment.onPictureInPictureModeChanged().
Android 15 wprowadza zmiany, które zapewniają jeszcze płynniejsze przejście do trybu obrazu w obrazie. Jest to korzystne w przypadku aplikacji, które mają elementy interfejsu nakładane na główny interfejs, który przechodzi w tryb obrazu w obrazie.
Deweloperzy używają wywołania zwrotnego onPictureInPictureModeChanged(), aby zdefiniować logikę, która przełącza widoczność nakładanych elementów interfejsu. To wywołanie zwrotne jest aktywowane 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. W trybie obrazu w obrazie wiele elementów interfejsu nie jest istotnych dla aplikacji. Na przykład 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 okna obrazu w obrazie, użyj wywołania zwrotnego onPictureInPictureModeChanged(), aby odkryć te elementy, jak pokazano w tych przykładach:
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
if (pipState.isTransitioningToPip()) {
// Hide UI elements.
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (!isInPictureInPictureMode) {
// Unhide UI elements.
}
}
Ten przełącznik szybkiej 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 interakcję 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 użytkownikom największą wygodę.
Jeśli Twoja aplikacja musi udostępniać niestandardowe działania w trybie obraz 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ę przyciski odtwarzania, wstrzymywania, następny i poprzedni.
Możesz też 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ść przejdzie do trybu obrazu w obrazie, system umieści ją w stanie wstrzymania i wywoła metodę onPause(). Odtwarzanie filmu nie powinno być wstrzymywane, ale kontynuowane, jeśli aktywność zostanie wstrzymana podczas przechodzenia do trybu obraz w obrazie.
W Androidzie 7.0 (poziom interfejsu API 24) 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. Przykład:
override fun onPause() {
super.onPause()
// If called while in PiP mode, don't 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. Odtwarzaj 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.
Obsługa trybu obrazu w obrazie w aplikacjach aparatu
Aby włączyć tryb obrazu w obrazie w aplikacjach aparatu, upewnij się, że aparat pozostaje aktywny w trybie obrazu w obrazie, nie zamykając go po wywołaniu funkcji onPause():
override fun onPause() {
super.onPause()
// Don't close the camera if the app is entering PiP mode
if (!isInPictureInPictureMode) {
closeCamera()
}
}
Podobnie jak w przypadku innych zastosowań ukryj nieistotne elementy interfejsu (np. elementy sterujące i nakładki) oraz dodaj niestandardowe działania, aby sterować kamerą (np. zatrzymać nagrywanie lub przełączyć kamerę).
Obliczanie wartości sourceRectHint w celu zapewnienia płynnych przejść
Podanie dokładnego sourceRectHint z precyzyjnymi współrzędnymi ekranu wizjera aparatu jest niezbędne do płynnego wyświetlania animacji wejścia. Granice widoku podglądu możesz uzyskać za pomocą getGlobalVisibleRect() w ten sposób:
val previewView = findViewById<View>(R.id.preview_view)
val globalRect = Rect()
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect)
val params = PictureInPictureParams.Builder()
.setSourceRectHint(globalRect)
.build()
setPictureInPictureParams(params)
Uwagi dodatkowe
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 działań, które odtwarzają wideo pełnoekranowe. 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 Co jest grane.
Gdy aplikacja jest w trybie obrazu w obrazie, 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 artykule Zarządzanie aktywnością audio. Jeśli w trybie PIP otrzymasz powiadomienie o utracie aktywności audio, wstrzymaj lub zatrzymaj odtwarzanie filmu.
Gdy aplikacja ma przejść do trybu obrazu w obrazie, pamiętaj, że tylko aktywność na górze przechodzi do tego trybu. W niektórych sytuacjach, np. na urządzeniach z wieloma oknami, może się zdarzyć, że podstawowa aktywność zostanie ponownie wyświetlona i będzie widoczna obok aktywności w trybie obrazu w obrazie. W takim przypadku należy podjąć odpowiednie działania, w tym wywołać funkcję zwrotną onResume() lub onPause() dla powiązanej aktywności.
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ść odtwarzanego 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).