Dodawanie filmów przy użyciu obrazu w obrazie (PIP)

Wypróbuj tworzenie wiadomości
Jetpack Compose to zalecany zestaw narzędzi interfejsu na Androida. Dowiedz się, jak obsługiwać obraz w obrazie w narzędziu Compose

Począwszy od Androida 8.0 (poziom interfejsu API 26) Android zezwala na uruchamianie działań w trybie obrazu w obrazie (PIP). Tryb PiP to specjalny tryb wielookienkowy, który jest używany głównie do 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.

Tryb PiP korzysta z interfejsów API dla wielu okien dostępnych w Androidzie 7.0, aby wyświetlać przypięte okno z filmem. Aby dodać PiP do aplikacji, musisz zarejestrować czynności, które obsługują PiP, odpowiednio przełączyć czynności w tryb PiP i zadbać o to, aby elementy interfejsu były ukryte, a odtwarzanie filmu było kontynuowane, gdy czynność jest w trybie PiP.

Okno PiP pojawi się na najwyższej warstwie ekranu w rogu wybranym przez system.

Obraz w obrazie jest też obsługiwany na zgodnych urządzeniach z systemem operacyjnym Android TV i z systemem Android 14 (poziom interfejsu API 34) lub nowszym. Chociaż istnieje wiele podobieństw, podczas korzystania z okna PIP na telewizorze należy wziąć pod uwagę dodatkowe kwestie.

Jak użytkownicy mogą korzystać z okna 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 trybu pełnoekranowego, przycisk Zamknij, przycisk ustawień i niestandardowe działania udostępniane przez aplikację (np. elementy sterujące odtwarzaniem).

  • Kliknij okno dwukrotnie, aby przełączyć się między bieżącym a maksymalnym lub minimalnym rozmiarem obrazu w obrazie. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna spowoduje jego zminimalizowanie, a odwrotnie.

  • 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.

  • Zmień rozmiar okna PiP, używając funkcji powiększania.

Twoja aplikacja określa, kiedy bieżąca aktywność przejdzie w tryb obrazu w powietrzu. 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. Dzięki temu Mapy Google mogą wyświetlać wskazówki, gdy użytkownik wykonuje jednocześnie inną czynność.

  • Aplikacja może przenieść film do trybu PiP, gdy użytkownik przejdzie z niego, aby przeglądać inne treści.

  • Aplikacja może przełączyć film do trybu obrazu w obrazie, gdy użytkownik ogląda koniec odcinka treści. Ekran główny zawiera informacje promocyjne lub podsumowanie dotyczące następnego odcinka serii.

  • Aplikacja pozwala użytkownikom dodawać kolejne treści do kolejki podczas oglądania filmu. Film będzie nadal odtwarzany w trybie obrazu w obrazie, a na ekranie głównym wyświetli się aktywność związana z wyborem treści.

Zadeklaruj obsługę obrazu w obrazie

Domyślnie system nie obsługuje automatycznie funkcji PIP w aplikacjach. Jeśli chcesz, aby Twoja aplikacja obsługiwała tryb obrazu w pliku, zarejestruj aktywność związaną z wideo w pliku manifestu, ustawiając wartość android:supportsPictureInPicture na true. Pamiętaj też, aby w swojej aktywności uwzględnić zmiany konfiguracji układu, aby nie uruchamiała się ponownie, gdy nastąpi zmiana układu podczas przełączania do trybu PiP.

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

Przełączanie aktywności do trybu obrazu w obrębie obrazu

Od Androida 12 możesz przełączyć aktywność do trybu obrazu w obrazie, ustawiając flagę setAutoEnterEnabled na true. Dzięki temu ustawieniu aktywność automatycznie przełącza się na tryb PiP w razie potrzeby bez konieczności wywoływania funkcji enterPictureInPictureMode()onUserLeaveHint. Ma to też dodatkową zaletę: znacznie płynniejsze przejścia. Więcej informacji znajdziesz w artykule Ułatwianie płynnego przechodzenia do trybu obrazu w obrazie za pomocą nawigacji za pomocą gestów.

Jeśli kierujesz aplikację na Androida 11 lub niższego, aktywność musi wywołać funkcję enterPictureInPictureMode(), aby przejść do trybu obrazu w obrazie. Na przykład poniższy 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 użyć logiki, która przełączy aktywność w tryb PiP zamiast w tło. Na przykład Mapy Google przełączają się w tryb PiP, gdy użytkownik naciśnie przycisk Początek lub Ostatnie podczas nawigacji. Możesz to zrobić, zastępując 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

Android 12 wprowadził znaczne ulepszenia wizualne animowanych przejść między oknami pełnoekranowymi a oknami w trybie PiP. Zdecydowanie zalecamy wdrożenie wszystkich zalecanych zmian. Po ich wprowadzeniu będą one automatycznie skalowane na duże ekrany, takie jak składane urządzenia 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ć zniknięcie okna PiP podczas przejścia, a następnie jego ponowne pojawienie się po zakończeniu przejścia.

Te zmiany obejmują:

  • Ulepszone przechodzenie do trybu obrazu w oknie z użyciem gestów
  • Ustawienie odpowiedniego sourceRectHint do włączania i wyłączania trybu obrazu w obrazie
  • Wyłączanie płynnego dostosowywania rozmiaru w przypadku treści innych niż filmy

Aby uzyskać informacje o umożliwianiu płynnych przejść, zapoznaj się z przykładem kodu Kotlin PictureInPicture na Androida.

Ułatwienie płynnego przechodzenia do trybu obrazu w obrębie obrazu podczas korzystania z nawigacji za pomocą gestów

Od Androida 12 flaga setAutoEnterEnabled zapewnia znacznie płynniejszą animację podczas przechodzenia do treści wideo w reżymie obrazu w obrazie za pomocą gestów nawigacyjnych – np. gdy przesuwasz palcem w górę, aby wrócić do ekranu głównego z trybu pełnoekranowego.

Wykonaj poniższe czynności, aby wprowadzić tę zmianę, i zapoznaj się z tym przykładem:

  1. Aby utworzyć PictureInPictureParams.Builder, użyj elementu setAutoEnterEnabled:

    Kotlin

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())
    

    Java

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
    
  2. Zadzwoń pod numer setPictureInPictureParams, aby poinformować o aktualnym stanie PictureInPictureParams. Aplikacja nie czeka na wywołanie zwrotne onUserLeaveHint (tak jak w Androidzie 11).

    Możesz na przykład wywołać funkcję setPictureInPictureParams podczas pierwszego odtwarzania i przy każdym kolejnym odtwarzaniu, jeśli zmieni się format obrazu.

  3. Zadzwoń do setAutoEnterEnabled(false), ale tylko wtedy, gdy jest to konieczne. Na przykład prawdopodobnie nie chcesz włączyć trybu PIP, jeśli bieżące odtwarzanie jest wstrzymane.

Ustaw odpowiednią sourceRectHint na potrzeby włączania i wyłączania trybu obrazu w obrazie

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 sourceRectHint do implementowania znacznie płynniejszej animacji zarówno podczas wchodzenia do trybu PiP, jak i wychodzenia z niego.

Aby prawidłowo ustawić sourceRectHint na potrzeby wchodzenia do trybu obrazu w pliku i z niego wychodzenia:

  1. Zbuduj obiekt PictureInPictureParams, wyznaczając odpowiednie granice jako sourceRectHint. 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);
    
  2. W razie potrzeby zaktualizuj sourceRectHint, zanim system rozpocznie przenoszenie danych. Gdy system ma zamknąć tryb PiP, hierarchia widoku aktywności jest wyświetlana w ramach konfiguracji docelowej (np. pełny ekran). Aplikacja może dołączyć do widoku głównego lub docelowego (np. widoku odtwarzacza wideo) obiekt sourceRectHint, który będzie wykrywać zdarzenia i aktualizować go 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łączanie płynnego dostosowywania rozmiaru w przypadku treści innych niż filmy

Android 12 wprowadza flagę setSeamlessResizeEnabled, która zapewnia płynniejszą animację przejścia 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 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());

Zarządzaj interfejsem podczas obrazu w obrazie

Gdy aktywność wchodzi w tryb obrazu w obrazie lub go opuszcza, system wywołuje Activity.onPictureInPictureModeChanged() lub Fragment.onPictureInPictureModeChanged().

Android 15 wprowadza zmiany, które zapewniają jeszcze płynniejsze przechodzenie do trybu obrazu w płótnie. Jest to korzystne w przypadku aplikacji, które mają elementy UI nałożone na główny interfejs użytkownika i przechodzą do trybu PiP.

Deweloperzy używają wywołania zwrotnego onPictureInPictureModeChanged() do definiowania logiki, która przełącza widoczność nałożonych elementów interfejsu. Ten wywołanie zwrotne jest wywoływane po zakończeniu animacji otwierania lub zamykania PiP. Począwszy od Androida 15 klasa PictureInPictureUiState zawiera nowy stan.

W tym nowym stanie interfejsu aplikacje kierowane na Androida 15 obserwują wywołanie funkcji Activity#onPictureInPictureUiStateChanged()isTransitioningToPip(), gdy tylko rozpocznie się animacja PiP. W trybie obrazu w obrazie wiele elementów interfejsu nie jest istotnych dla aplikacji, np. widoki lub układy, które zawierają informacje takie jak sugestie, nadchodzące filmy, oceny i tytuły. Gdy aplikacja przejdzie w tryb PIP, użyj wywołania zwrotnego onPictureInPictureUiStateChanged(), aby ukryć te elementy interfejsu. Gdy aplikacja przejdzie z okna PiP do trybu pełnoekranowego, użyj wywołania zwrotnego onPictureInPictureModeChanged(), aby odsłonić 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.
        }
    }

Szybkie przełączanie widoczności nieistotnych elementów interfejsu (w przypadku okna PiP) pomaga zapewnić płynniejszą i bezmigotnościową animację otwierania PiP.

Zastąp te funkcje zwracające wartości, aby ponownie narysować elementy interfejsu użytkownika 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ć słabo widoczne. Najlepsze wrażenia użytkownika zapewniają działania związane z odtwarzaniem filmów z minimalnym interfejsem.

Jeśli Twoja aplikacja musi udostępniać działania niestandardowe na potrzeby obrazu w obrazie, zapoznaj się z sekcją Dodawanie elementów sterujących na tej stronie. Usuń inne elementy interfejsu, zanim aktywność przejdzie do obrazu w obrazie, i przywróć je, gdy aktywność znów będzie wyświetlana na pełnym ekranie.

Dodaj elementy sterujące

Okno PiP 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, przewijaniem do przodu i wstecz.

Możesz też określić działania niestandardowe w sposób jawny, tworząc element PictureInPictureParams za pomocą elementu PictureInPictureParams.Builder.setActions() przed przejściem do trybu obrazu w obrazie i przekazując parametry podczas przechodzenia do tego trybu za pomocą elementu enterPictureInPictureMode(android.app.PictureInPictureParams) lub setPictureInPictureParams(android.app.PictureInPictureParams). Zachowaj ostrożność. Jeśli spróbujesz dodać więcej niż getMaxNumPictureInPictureActions(), będziesz mieć tylko maksymalną liczbę.

Kontynuowanie odtwarzania filmu w trybie PiP

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, a zamiast tego powinno być kontynuowane, jeśli aktywność została wstrzymana podczas przejścia do trybu PiP.

W Androidzie 7.0 i nowszych należy wstrzymać i wznowić odtwarzanie filmu, gdy system wywoła metodę onStop() lub onStart(). 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 ma wartości true i chcesz wstrzymać odtwarzanie w implementacji onPause(), sprawdź tryb PiP, wywołując funkcję 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 PiP do trybu pełnoekranowego, 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 głównym ekranie, gdy odtwarzanie filmu odbywa się w trybie obrazu w obrazie. odtwarzać nowy film w ramach dotychczasowej aktywności odtwarzania w trybie pełnoekranowym, zamiast uruchamiać nową aktywność, która może zdezorientować użytkownika;

Aby zapewnić, że do obsługi żądań odtwarzania filmów używana jest jedna aktywność, a w razie potrzeby przełączana jest w tryb PiP lub z niego wyjmowana, ustaw w pliku manifestu android:launchMode na singleTask:

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

W swojej aktywności zastąpij onNewIntent() i obsługuj nowy film, w razie potrzeby przerywając odtwarzanie dotychczasowego.

Sprawdzone metody

Tryb obrazu w obrazie może być wyłączony na urządzeniach z niewielką ilością pamięci RAM. Zanim aplikacja zacznie korzystać z trybu PiP, sprawdź, czy jest on dostępny, wywołując funkcję hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

Tryb PiP jest przeznaczony do czynności, które wymagają odtwarzania filmów w trybie pełnoekranowym. Podczas przełączania aktywności do trybu PiP nie pokazuj niczego poza treścią wideo. Śledź, kiedy Twoja aktywność przechodzi w tryb PiP, i ukrywaj elementy interfejsu zgodnie z opisem w sekcji Zarządzanie interfejsem w trybie PiP.

Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie jest zaznaczona. Aby otrzymywać zdarzenia wejścia w trybie PiP, użyj elementu 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 po rozpoczęciu odtwarzania filmu i obsługuj powiadomienia o zmianie fokusu, jak opisano w sekcji Zarządzanie ostrością dźwięku. Jeśli w trybie PiP otrzymasz powiadomienie o utracie skupienia dźwięku, wstrzymaj lub zatrzymaj odtwarzanie filmu.

Gdy aplikacja ma przejść do trybu PiP, zwróć uwagę, że tylko aktywna czynność jest przenoszona do obrazu w obrazie. W niektórych sytuacjach, np. na urządzeniach z wieloma oknami, możliwe jest, że aktywność poniżej będzie widoczna obok PiP. Należy odpowiednio zająć się tym problemem, łącznie z poniższą czynnością związaną z otrzymywaniem wywołania zwrotnego onResume() lub onPause(). Możliwe też, że użytkownik wejdzie w interakcję z treścią. Jeśli na przykład widzisz aktywność związaną z listą filmów, a odtwarzanie filmu w trybie obrazu 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 języku Kotlin, zapoznaj się z artykułem Przykładowa aplikacja Android PictureInPicture (Kotlin).