Począwszy od Androida 8.0 (poziom interfejsu API 26) Android zezwala na uruchamianie działań w trybie obrazu w obrazie (PIP). PIP to specjalny typ trybu wielu okien, który jest najczęściej używany podczas odtwarzania filmów. Dzięki niej użytkownik może obejrzeć film w małym oknie przypiętym w rogu ekranu i jednocześnie poruszać się między aplikacjami lub przeglądać treści na ekranie głównym.
PIP wykorzystuje interfejsy API trybu wielu okien dostępne w Androidzie 7.0 do wyświetlania przypiętego okna nakładki wideo. Aby dodać tę funkcję do aplikacji, musisz zarejestrować działania, które obsługują tę funkcję, w razie potrzeby przełączyć aktywność na tryb obraz w obrazie i upewnić się, że elementy interfejsu są ukryte, a film będzie nadal odtwarzany w trybie obraz w obrazie.
Okno to wyświetla się w górnej warstwie ekranu, w rogu wybranym przez system.
Obraz w obrazie jest też obsługiwany na zgodnych urządzeniach z systemem operacyjnym Android TV z Androidem 14 (poziom interfejsu API 34) lub nowszym. Istnieje wiele podobieństw, ale podczas korzystania z PiP w telewizji należy wziąć pod uwagę dodatkowe kwestie.
Jak użytkownicy mogą korzystać z okna obrazu w obrazie
Użytkownicy mogą przeciągnąć okno PIP w inne miejsce. Począwszy od Androida 12 użytkownicy mogą też:
Kliknij raz okno, aby wyświetlić przełącznik trybu pełnoekranowego, przycisk zamykania, przycisk ustawień i niestandardowe działania dostępne w aplikacji (np. sterowanie odtwarzaniem).
Kliknij dwukrotnie okno, aby przełączyć się między bieżącym a maksymalnym lub minimalnym rozmiarem obrazu w obrazie. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna powoduje jego zminimalizowanie. Obowiązuje też odwrotność.
Ukryj okno, przeciągając je do lewej lub prawej krawędzi. Aby przywrócić okno ze schowka, kliknij widoczną część okna ze schowka lub przeciągnij je poza nie.
Powiększ lub ściągnij palce, aby zmienić rozmiar okna funkcji obraz w obrazie.
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. W ten sposób Mapy Google nadal wyświetlają wskazówki dojazdu, gdy użytkownik wykonuje inną czynność w tym samym czasie.
Aplikacja może przenieść film do trybu obraz w obrazie, gdy użytkownik wróci z filmu, aby przejrzeć inne treści.
Twoja aplikacja może przełączać film w tryb obrazu w obrazie, gdy użytkownik ogląda do końca odcinka treści. Ekran główny zawiera informacje promocyjne lub podsumowujące następny odcinek.
Aplikacja pozwala użytkownikom dodawać kolejne treści do kolejki podczas oglądania filmu. W trybie PIP film jest nadal odtwarzany, gdy na ekranie głównym wyświetla się aktywność dotycząca wyboru treści.
Zadeklaruj obsługę obrazu w obrazie
Domyślnie system nie obsługuje automatycznie funkcji PIP w aplikacjach. Jeśli chcesz obsługiwać tę funkcję w swojej aplikacji, zarejestruj aktywność związaną z filmami w pliku manifestu, ustawiając 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łącz aktywność na obraz w obrazie
Począwszy od Androida 12, można włączyć tryb 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
. Ma to też dodatkową zaletę:
znacznie płynniejsze przejścia. Więcej informacji znajdziesz w artykule Płynniejsze przejście do trybu obraz w obrazie dzięki nawigacji przy użyciu gestów.
Jeśli kierujesz aplikację na Androida 11 lub starszego, aktywność musi wywołać metodę enterPictureInPictureMode()
, aby przełączyć się w tryb PIP. Na przykład ten kod przełącza aktywność w tryb PiP, gdy użytkownik kliknie specjalny 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 dodać logikę, która przełącza aktywność w tryb PIP zamiast przenoszenia jej w tle. Na przykład Mapy Google przełączają się w tryb obrazu w obrazie, jeśli użytkownik naciśnie przycisk ekranu głównego lub przycisk Ostatnie podczas korzystania z aplikacji. 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 dopracowane przejście w trybie PIP
W Androidzie 12 wprowadzono istotne poprawki kosmetyczne w animowanych przejściach między pełnym ekranem a oknami PIP. Zdecydowanie zalecamy wprowadzenie wszystkich odpowiednich zmian. Gdy to zrobisz, zostaną one 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 w trybie PIP nadal działają, ale animacje są mniej dopracowane. Na przykład przejście z trybu pełnoekranowego do trybu PIP może spowodować, że okno obrazu w obrazie znika podczas przejścia, zanim pojawi się ono ponownie po zakończeniu tego przejścia.
Obejmują one następujące elementy.
- Płynniejsze przejście z nawigacji przy użyciu gestów do trybu obraz w obrazie
- Ustawiam właściwą wartość
sourceRectHint
umożliwiającą włączanie i wyłączanie trybu obraz w obrazie - Wyłączanie płynnej zmiany rozmiaru w przypadku treści innych niż wideo
Aby uzyskać dopracowane przejście, możesz skorzystać z przykładu PictureInPicture na Androida z Kotlin.
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:
Użyj
setAutoEnterEnabled
, aby utworzyć obiektPictureInPictureParams.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ń pod numer
setPictureInPictureParams
pod aktualne informacje oPictureInPictureParams
wcześniej. Aplikacja nie czeka na wywołanie zwrotneonUserLeaveHint
(tak jak w Androidzie 11).Możesz na przykład wywoływać funkcję
setPictureInPictureParams
przy pierwszym odtworzeniu i kolejnym odtworzeniu, gdy współczynnik proporcji ulegnie zmianie.Zadzwoń pod numer
setAutoEnterEnabled(false)
, ale tylko wtedy, gdy jest to konieczne. Prawdopodobnie nie chcesz na przykład włączać trybu obraz w obrazie, jeśli bieżące odtwarzanie jest wstrzymane.
Ustaw odpowiednią wartość sourceRectHint
umożliwiającą włączanie i wyłączanie trybu PIP
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 interfejsu sourceRectHint
, aby zapewnić znacznie płynniejsze animacje zarówno po przejściu do trybu obrazu w obrazie, jak i po jego wyjściu.
Aby prawidłowo ustawić sourceRectHint
na potrzeby włączania i wyłączania trybu obraz w obrazie:
Zbuduj obiekt
PictureInPictureParams
, wyznaczając odpowiednie granice jakosourceRectHint
. Zalecamy też załączenie do odtwarzacza 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 przenoszenie danych. Gdy system zamierza wyjść z trybu PIP, hierarchia widoku aktywności jest ułożona zgodnie z konfiguracją miejsca docelowego (na przykład na pełnym ekranie). Aplikacja może dołączyć detektor zmian układu do widoku głównego lub docelowego (np. widoku odtwarzacza), aby wykryć zdarzenie i zaktualizowaćsourceRectHint
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łącz płynną zmianę rozmiaru w przypadku treści innych niż wideo
Android 12 dodaje flagę setSeamlessResizeEnabled
, która zapewnia płynniej płynną animację przenikającą 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ć 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 obrazu w obrazie
Gdy aktywność wchodzi w tryb PIP lub z niego opuszcza, system wywołuje Activity.onPictureInPictureModeChanged()
lub Fragment.onPictureInPictureModeChanged()
.
Te wywołania zwrotne należy zastąpić, aby ponownie wykorzystać elementy interfejsu aktywności. Pamiętaj, że w trybie PIP aktywność jest wyświetlana w małym oknie. W trybie obrazu w obrazie użytkownicy nie mogą wchodzić w interakcje z elementami interfejsu aplikacji, a szczegóły małych elementów interfejsu mogą być słabo widoczne. Czynności związane z odtwarzaniem filmów przy minimalnym interfejsie użytkownika zapewniają użytkownikom najlepsze wrażenia.
Jeśli Twoja aplikacja musi udostępniać niestandardowe działania dla obrazu w obrazie, zapoznaj się z sekcją Dodawanie elementów sterujących na tej stronie. Usuń inne elementy interfejsu, zanim Twoja aktywność przejdzie w obraz w widoku obrazu, i przywróć je, gdy aktywność znów przejdzie w tryb pełnego ekranu:
Kotlin
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { if (isInPictureInPictureMode) { // Hide the full-screen UI (controls, etc.) while in PiP mode. } else { // Restore the full-screen UI. } }
Java
@Override public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) { if (isInPictureInPictureMode) { // Hide the full-screen UI (controls, etc.) while in PiP mode. } else { // Restore the full-screen UI. ... } }
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ę multimediów, pojawią się elementy sterujące odtwarzaniem, wstrzymywaniem, Dalej i poprzedni.
Możesz też bezpośrednio określić działania niestandardowe, tworząc PictureInPictureParams
za pomocą PictureInPictureParams.Builder.setActions()
przed włączeniem trybu obraz w obrazie i przekazując parametry po przejściu w tryb obrazu w postaci obrazu za pomocą funkcji 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ę.
Kontynuuję odtwarzanie filmu w trybie obraz w obrazie
Gdy aktywność przełącza się na obraz w obrazie, system ustawia ją w stanie wstrzymania i wywołuje metodę onPause()
tej aktywności. Odtwarzanie filmu nie powinno być wstrzymywane. Zamiast tego odtwarzanie powinno być kontynuowane, jeśli aktywność jest wstrzymana w czasie 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 unikniesz sprawdzania, czy aplikacja jest w trybie Obraz w obrazie w onPause()
, i dalszego odtwarzania.
Jeśli flaga setAutoEnterEnabled
nie jest ustawiona na true
i chcesz wstrzymać odtwarzanie w implementacji onPause()
, sprawdź tryb obrazu w obrazie, wywołując parametr isInPictureInPictureMode()
i odpowiednio obsługuj 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ść przełączy się z powrotem na tryb pełnoekranowy, system wznowi aktywność i wywoła metodę onResume()
.
Używaj pojedynczej aktywności związanej z odtwarzaniem na potrzeby funkcji PIP
W Twojej aplikacji użytkownik może wybrać nowy film podczas przeglądania treści na ekranie głównym, 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 mieć pewność, że pojedyncza aktywność jest używana do żądań odtwarzania filmów i w razie potrzeby przełącza się na tryb PIP lub z niego wyłącza, w pliku manifestu ustaw 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 istniejącego filmu.
Sprawdzone metody
Obraz w obrazie może być wyłączony na urządzeniach z małą 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. Gdy przełączasz aktywność w tryb PIP, unikaj wyświetlania niczego oprócz treści wideo. Śledź, gdy aktywność przechodzi w tryb PIP i ukrywa elementy interfejsu zgodnie z opisem w sekcji Obsługa interfejsu podczas obrazu w obrazie.
Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie jest ona zaznaczona. Aby odbierać zdarzenia wejściowe w trybie PIP, użyj MediaSession.setCallback()
.
Więcej informacji o korzystaniu z funkcji setCallback()
znajdziesz w artykule Wyświetlanie karty Co jest grane.
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 w momencie rozpoczęcia 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ść w tryb PIP, pamiętaj, że w obrazie w obrazie jest wyświetlana tylko najwyższa aktywność. W niektórych sytuacjach, na przykład na urządzeniach z wieloma oknami, może się zdarzyć, że poniższa aktywność będzie teraz widoczna i ponownie widoczna obok aktywności funkcji obraz w obrazie. Rozwiąż ten problem w odpowiedni sposób, łącznie z poniższą czynnością związaną z otrzymywaniem wywołań zwrotnych onResume()
lub onPause()
. Możliwe jest też, że użytkownik wchodzi w interakcję z działaniem. Jeśli na przykład widzisz aktywność z listy filmów, a odtwarzanie filmu w trybie obraz 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 Androidzie, zapoznaj się z przykładem obrazu w obrazie. Aby pobrać przykładową aplikację napisaną w Kotlin, zobacz Android PictureInPicture Sample (Kotlin).