Od Androida 8.0 (poziom API 26) Android umożliwia uruchamianie aktywności w trybie obrazu w obrazie (PiP). Obraz w obrazie to specjalny rodzaj trybu wielu okien używany do odtwarzania filmów, rozmów wideo i nawigacji. Umożliwia użytkownikowi przypięcie istniejącego okna aktywności do rogu ekranu podczas przełączania się między aplikacjami lub przeglądania treści na ekranie głównym.
Tryb obrazu w obrazie wykorzystuje interfejsy API wielu okien dostępne w Androidzie 7.0, aby wyświetlać przypięte okno nakładki wideo. Aby dodać tryb obrazu w obrazie do aplikacji, musisz zarejestrować aktywności, które go obsługują, przełączyć aktywność na tryb obrazu w obrazie w razie potrzeby i upewnić się, że elementy interfejsu są ukryte, a odtwarzanie filmu jest kontynuowane, gdy aktywność jest w trybie obrazu w obrazie.
Okno obrazu w obrazie pojawia się w najwyższej warstwie ekranu, w rogu wybranym przez system.
Funkcja PiP jest też obsługiwana na zgodnych urządzeniach z systemem operacyjnym Android TV w wersji 14 (poziom interfejsu API 34) lub nowszej. Chociaż istnieje wiele podobieństw, podczas korzystania z obrazu w obrazie na telewizorze należy wziąć pod uwagę dodatkowe kwestie.
Jak użytkownicy mogą korzystać z okna obrazu w obrazie
Użytkownicy mogą przeciągnąć okno obrazu w obrazie w inne miejsce. Od Androida 12 użytkownicy mogą też:
Kliknij raz 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 odtwarzaczem).
Kliknij dwukrotnie okno, aby przełączyć się między bieżącym rozmiarem obrazu w obrazie a maksymalnym lub minimalnym rozmiarem obrazu w obrazie. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna spowoduje jego zminimalizowanie i odwrotnie.
Ukryj okno, przeciągając je do lewej lub prawej krawędzi. Aby przywrócić okno, kliknij jego widoczną część lub przeciągnij je na ekran.
Zmień rozmiar okna PiP, używając gestu rozciągania.
Aplikacja kontroluje, 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 kliknie przycisk ekranu głównego lub przesunie palcem w górę, aby przejść do ekranu głównego. Dzięki temu Mapy Google mogą wyświetlać wskazówki dojazdu, gdy użytkownik wykonuje jednocześnie inną czynność.
Aplikacja może przełączyć film w tryb obrazu w obrazie, gdy użytkownik wróci z filmu do przeglądania innych 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świetlane są 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. Odtwarzanie filmu będzie kontynuowane w trybie obrazu w obrazie, a na ekranie głównym wyświetli się aktywność wyboru treści.
Deklarowanie obsługi PiP
Domyślnie system nie obsługuje automatycznie obrazu w obrazie w przypadku aplikacji. Jeśli chcesz, aby Twoja aplikacja obsługiwała tryb obrazu w obrazie, zarejestruj aktywność wideo w pliku manifestu, ustawiając android:supportsPictureInPicture na true. Określ też, że aktywność obsługuje zmiany konfiguracji układu, aby nie uruchamiała się ponownie, 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 PIP
Od Androida 12 możesz przełączyć aktywność na tryb obrazu w obrazie, ustawiając flagę setAutoEnterEnabled na true. Dzięki temu ustawieniu aktywność automatycznie przełącza się w tryb obrazu w obrazie, gdy jest to konieczne, bez konieczności jawnego wywoływania metody enterPictureInPictureMode() w onUserLeaveHint. Dodatkową zaletą jest to, że przejścia są znacznie płynniejsze. Więcej informacji znajdziesz w artykule Płynniejsze przechodzenie do trybu PIP przy użyciu nawigacji przy użyciu gestów.
Jeśli kierujesz aplikację na Androida 11 lub starszego, aktywność musi wywołać funkcję enterPictureInPictureMode(), aby przełączyć się na tryb obrazu w obrazie. Na przykład ten kod przełącza aktywność w tryb obrazu w obrazie, gdy użytkownik kliknie odpowiedni przycisk w interfejsie aplikacji:
override fun onActionClicked(action: Action) {
if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
activity?.enterPictureInPictureMode()
return
}
}
Możesz dodać logikę, która przełącza aktywność w tryb obrazu w obrazie zamiast w tryb działania w tle. Na przykład Mapy Google przełączają się w tryb obrazu w obrazie, gdy użytkownik naciśnie przycisk ekranu głównego lub ostatnich aplikacji podczas nawigacji. Możesz obsłużyć ten przypadek, zastępując metodę onUserLeaveHint():
override fun onUserLeaveHint() {
if (iWantToBeInPipModeNow()) {
enterPictureInPictureMode()
}
}
Zalecane: zapewnianie użytkownikom płynnego przejścia do trybu obrazu w obrazie
W Androidzie 12 wprowadzono znaczne ulepszenia wizualne animowanych przejść między oknami pełnoekranowymi i oknami PiP. Zdecydowanie zalecamy wprowadzenie wszystkich odpowiednich zmian. Po ich wprowadzeniu zmiany te zostaną automatycznie przeskalowane na duże ekrany, takie 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 obrazu w obrazie nadal działają, ale animacje są mniej dopracowane. Na przykład przejście z trybu pełnoekranowego do trybu obrazu w obrazie może spowodować, że okno obrazu w obrazie zniknie podczas przejścia, a następnie pojawi się ponownie po zakończeniu przejścia.
Zmiany obejmują:
- Płynniejsze przejścia do trybu obrazu w obrazie z nawigacji przy użyciu gestów
- Ustawianie prawidłowego elementu
sourceRectHintna potrzeby wchodzenia w tryb obrazu w obrazie i wychodzenia z niego - Wyłączanie płynnej zmiany rozmiaru w przypadku treści innych niż treści wideo
Aby dowiedzieć się, jak zapewnić płynne przejście, zapoznaj się z przykładem PictureInPicture w języku Kotlin na Androida.
Płynniejsze przechodzenie do trybu PiP z nawigacji przy użyciu gestów
Od Androida 12 flaga setAutoEnterEnabled zapewnia znacznie płynniejszą animację przejścia do treści wideo w trybie obrazu w obrazie przy użyciu nawigacji gestami – na przykład podczas przesuwania palcem w górę, aby przejść 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].
Wywołaj
setPictureInPictureParamsz aktualną wartościąPictureInPictureParamsna wczesnym etapie. Aplikacja nie czeka na wywołanie zwrotneonUserLeaveHint(jak w Androidzie 11).Możesz na przykład wywołać funkcję
setPictureInPictureParamspodczas pierwszego odtwarzania i każdego kolejnego, jeśli zmieni się format obrazu.Zadzwoń do:
setAutoEnterEnabled(false), ale tylko wtedy, gdy jest to konieczne. Na przykład prawdopodobnie nie chcesz włączać trybu obrazu w obrazie, jeśli odtwarzanie jest wstrzymane.
Ustaw odpowiedni element sourceRectHint na potrzeby wchodzenia w tryb obrazu w obrazie i wychodzenia z niego.
Od momentu wprowadzenia funkcji obrazu w obrazie w Androidzie 8.0 setSourceRectHint wskazuje obszar aktywności, który jest widoczny po przejściu do trybu obrazu w obrazie – na przykład granice widoku wideo w odtwarzaczu.
W Androidzie 12 system używa sourceRectHint, aby zapewnić znacznie płynniejszą animację zarówno podczas wchodzenia w tryb PiP, jak i wychodzenia z niego.
Aby prawidłowo skonfigurować sourceRectHint na potrzeby 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:
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 wyjścia. Gdy system ma wyjść z trybu obrazu w obrazie, hierarchia widoków aktywności jest układana zgodnie z konfiguracją docelową (np. pełny ekran). Aplikacja może dołączyć detektor zmiany układu do widoku głównego lub docelowego (np. widoku odtwarzacza), aby wykryć zdarzenie i zaktualizować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ż treści wideo
W Androidzie 12 dodano flagę setSeamlessResizeEnabled, która zapewnia znacznie płynniejszą animację przenikania podczas zmiany rozmiaru treści innych niż treści wideo w oknie obrazu w obrazie. Wcześniej zmiana rozmiaru treści innych niż treści wideo w oknie obrazu w obrazie mogła powodować powstawanie nieprzyjemnych dla oka artefaktów wizualnych.
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ść przechodzi w tryb obrazu w obrazie (PiP) lub go opuszcza, system wywołuje Activity.onPictureInPictureModeChanged() lub Fragment.onPictureInPictureModeChanged().
Android 15 wprowadza 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 nałożone na główny interfejs, który przechodzi w tryb PiP.
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 zwrotne Activity#onPictureInPictureUiStateChanged() z isTransitioningToPip(), gdy tylko rozpocznie się animacja obrazu w obrazie. Istnieje wiele elementów interfejsu, które nie są istotne 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 z okna obrazu w obrazie do trybu pełnoekranowego, 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.
}
}
Szybkie przełączanie widoczności nieistotnych elementów interfejsu (w oknie PiP) pomaga zapewnić płynniejszą i pozbawioną migotania animację wejścia w tryb PiP.
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. 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. Aktywności związane z odtwarzaniem filmów z minimalnym interfejsem zapewniają użytkownikom najlepsze wrażenia.
Jeśli aplikacja musi udostępniać działania niestandardowe w trybie obrazu w obrazie, zapoznaj się z sekcją Dodawanie elementów sterujących na tej stronie. Usuń inne elementy interfejsu, zanim aktywność przejdzie w tryb obrazu w obrazie, i przywróć je, gdy aktywność znów będzie wyświetlana na pełnym ekranie.
Dodaj elementy sterujące
Okno obrazu w obrazie może wyświetlać elementy sterujące, gdy użytkownik otworzy menu okna (klikając okno na urządzeniu mobilnym lub wybierając menu za pomocą pilota do telewizora).
Jeśli aplikacja ma aktywną sesję multimedialną, pojawią się elementy sterujące odtwarzaniem, wstrzymywaniem, przechodzeniem do następnego i poprzedniego utworu.
Możesz też jawnie określić działania niestandardowe, tworząc obiekt PictureInPictureParams za pomocą metody PictureInPictureParams.Builder.setActions() przed przejściem do trybu obrazu w obrazie i przekazując parametry podczas przechodzenia do trybu obrazu w obrazie za pomocą metody enterPictureInPictureMode(android.app.PictureInPictureParams) lub setPictureInPictureParams(android.app.PictureInPictureParams). Bądź ostrożny. Jeśli spróbujesz dodać więcej niż getMaxNumPictureInPictureActions(), zostanie dodana tylko maksymalna liczba.
kontynuowanie odtwarzania filmu w trybie obrazu w obrazie,
Gdy aktywność przełącza się na tryb obrazu w obrazie, system wstrzymuje ją i wywołuje metodę onPause(). Odtwarzanie filmu nie powinno być wstrzymywane, ale kontynuowane, jeśli aktywność zostanie wstrzymana podczas przechodzenia do trybu obrazu w obrazie.
W Androidzie 7.0 (poziom interfejsu API 24) i nowszych wersjach należy wstrzymywać i wznawiać odtwarzanie filmu, gdy system wywołuje metody onStop() i onStart() 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 jest ustawiona na true, a musisz wstrzymać odtwarzanie w implementacji onPause(), sprawdź, czy tryb obrazu w obrazie jest włączony, 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 obrazu w obrazie
W aplikacji użytkownik może wybrać nowy film podczas przeglądania treści na ekranie głównym, gdy odtwarzanie filmu jest w trybie obrazu w obrazie. Odtwarzaj nowy film w dotychczasowej aktywności odtwarzania w trybie pełnoekranowym, zamiast uruchamiać nową aktywność, która może dezorientować użytkownika.
Aby mieć pewność, że do żądań odtwarzania filmu jest używana jedna aktywność i że w razie potrzeby jest ona przełączana w tryb obrazu w obrazie lub z niego, 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 dotychczasowego.
Obsługa 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 aparatu po wywołaniu 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 przypadków użycia, ukryj nieistotne elementy interfejsu (takie jak elementy sterujące i nakładki) i dodaj działania niestandardowe do sterowania aparatem (np. zatrzymanie nagrywania lub odwrócenie aparatu).
Obliczanie sourceRectHint na potrzeby płynnych przejść
Podanie dokładnego obiektu sourceRectHint z dokładnymi współrzędnymi ekranu wizjera aparatu jest niezbędne do płynnego wyświetlania animacji wejścia. Możesz uzyskać granice z widoku podglądu 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 małą ilością pamięci RAM. Zanim aplikacja użyje funkcji obrazu w obrazie, sprawdź, czy jest ona dostępna, wywołując metodę 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 PIP unikaj wyświetlania innych treści niż treści wideo. śledzić, kiedy aktywność przechodzi w tryb obrazu w obrazie, i ukrywać elementy interfejsu, jak opisano w sekcji Obsługa interfejsu w trybie obrazu w obrazie;
Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie jest zaznaczona. Aby odbierać zdarzenia wejściowe w trybie obrazu w obrazie, użyj MediaSession.setCallback().
Więcej informacji o korzystaniu z setCallback() znajdziesz w artykule Wyświetlanie karty Co jest grane.
Gdy aplikacja jest w trybie obrazu w obrazie, odtwarzanie filmu w oknie obrazu w obrazie może powodować zakłócenia dźwięku w innej aplikacji, np. odtwarzaczu muzyki lub aplikacji do wyszukiwania głosowego. Aby tego uniknąć, poproś o zaznaczenie dźwięku, gdy zaczynasz odtwarzać film, i obsługuj powiadomienia o zmianie zaznaczenia dźwięku, jak opisano w artykule Zarządzanie zaznaczeniem dźwięku. Jeśli w trybie PiP otrzymasz powiadomienie o utracie fokusu audio, wstrzymaj lub zatrzymaj odtwarzanie filmu.
Gdy aplikacja ma przejść w tryb obrazu w obrazie, pamiętaj, że tylko aktywność na górze przechodzi w ten tryb. W niektórych sytuacjach, np. na urządzeniach z wieloma oknami, możliwe jest, że aktywność podrzędna będzie 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 ramach aktywności bazowej.
Możliwe jest też, że użytkownik wejdzie w interakcję z aktywnością.
Jeśli na przykład masz aktywność listy filmów i aktywność odtwarzania filmu w trybie obrazu w obrazie, użytkownik może wybrać nowy film z listy, a aktywność obrazu w obrazie powinna zostać odpowiednio zaktualizowana.
Dodatkowy przykładowy kod
Aby pobrać przykładową aplikację napisaną w języku Kotlin, zobacz przykładową aplikację Android PictureInPicture (Kotlin).