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ć tryb obrazu w obrazie w aplikacji Redagowanie.

Począwszy od Androida w wersji 8.0 (interfejs API na poziomie 26), Android pozwala uruchamiać aktywności w trybie obraz w obrazie (PiP). Tryb PiP to specjalny tryb wielookienkowy, który jest używany głównie do odtwarzania filmów. Pozwala użytkownikowi oglądać film w małym oknie przypiętym do rogu ekranu, a jednocześnie przełączać inne aplikacje lub wyszukiwać treści na głównym ekranie.

Tryb PiP korzysta z interfejsów API obsługiwanych przez wiele okien, które są dostępne 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.

Tryb PiP jest też obsługiwany na zgodnych urządzeniach z Androidem TV z Androidem 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ą 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 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 rozmiarem PiP lub między minimalnym a maksymalnym rozmiarem PiP. Na przykład dwukrotne kliknięcie zmaksymalizowanego okna spowoduje jego zminimalizowanie, a na odwrót.

  • Ukryj okno, przeciągając je w lewo lub w prawo. Aby odsłonić okno, kliknij widoczną część ukrytego okna lub przeciągnij je.

  • Zmieniaj rozmiar okna PiP za pomocą funkcji „Ściągnij lub rozciągnij palce”.

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 kliknie przycisk ekranu głównego lub przesunie palcem w górę, aby otworzyć 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 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 wyświetli się aktywność związana z wyborem treści.

Deklarowanie obsługi obrazu w powielaniu

Domyślnie system nie obsługuje automatycznie trybu PiP w przypadku aplikacji. 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 podczas zmiany 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 oknie

Od Androida 12 możesz przełączyć aktywność do trybu PiP, 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. Dodatkową zaletą jest to, że przejścia są płynniejsze. Więcej informacji znajdziesz w artykule Ułatwianie płynnego przechodzenia do trybu obrazu w powietrzu za pomocą gestów.

Jeśli kierujesz aplikację na Androida 11 lub niższego, aktywność musi wywołać funkcję enterPictureInPictureMode(), aby przełączyć się 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 złapać to zgłoszenie, zastępując je: onUserLeaveHint():

Kotlin

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

Zalecane: zapewnij użytkownikom płynne przejście do trybu 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 będą działać, ale animacje będą 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 w nawigacji za pomocą gestów

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

Aby wprowadzić tę zmianę, wykonaj podane niżej czynności i jako punkt odniesienia użyj tego przykładu:

  1. Aby utworzyć PictureInPictureParams.Builder, użyj 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 onUserLeaveHint (jak to miało miejsce 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

Po wprowadzeniu funkcji PiP w Androidzie 8.0 setSourceRectHintokreślała obszar aktywności widoczny po przejściu do obrazu w obrazie, na przykład obszar widoku 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. Utwórz PictureInPictureParams, używając odpowiednich ograniczeń jako sourceRectHint. Zalecamy też dołączenie do odtwarzacza wideo listenera 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 przed rozpoczęciem przez system przejścia wyjścia. 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) detektor zmiany układu, 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łą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ć bezszwonne dostosowywanie rozmiaru w przypadku treści innych niż filmy:

Kotlin

setPictureInPictureParams(PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build())

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build());

Obsługa interfejsu podczas korzystania z PIP

Gdy aktywność wchodzi w tryb obraz w obrazie lub z niego wychodzi, 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. To wywołanie zwrotne jest wywoływane po zakończeniu animacji wchodzenia lub wychodzenia z obrazu w obrazie. 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. Dotyczy to na przykład widoków lub układu zawierających 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 interakcje z elementami interfejsu aplikacji, gdy jest ona w trybie obrazu w obrazie, a szczegóły małych elementów interfejsu mogą być niewidoczne. Najlepsze wrażenia użytkownika zapewniają działania związane z odtwarzaniem filmów z minimalnym interfejsem.

Jeśli Twoja aplikacja musi wykonywać niestandardowe działania w ramach PiP, na tej stronie przeczytaj sekcję Dodawanie elementów sterujących. 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ść przejdzie do trybu PiP, system wstrzyma aktywność i wywoła 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ź, czy jest dostępny tryb PiP, wywołując funkcję isInPictureInPictureMode(), i odpowiednio obsłuż odtwarzanie. 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 z powrotem do trybu pełnoekranowego, system wznowi aktywność i wywoła metodę onResume().

Używanie jednej aktywności odtwarzania w przypadku 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 ukrywanie elementów interfejsu zgodnie z opisem w sekcji Zarządzanie interfejsem w trybie PiP.

Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie ma aktywnego pola tekstowego. Aby otrzymywać zdarzenia wejścia w trybie PiP, użyj elementu MediaSession.setCallback(). Więcej informacji o używaniu setCallback() znajdziesz w artykule Wyświetlanie karty Obecnie odtwarzane.

Gdy aplikacja jest w trybie PiP, odtwarzanie filmu w oknie PiP może powodować zakłócenia dźwięku w innej aplikacji, np. w odtwarzaczu muzycznym lub aplikacji do wyszukiwania głosowego. Aby tego uniknąć, poproś o uzyskanie kontroli nad dźwiękiem podczas odtwarzania filmu i obserwuj powiadomienia o zmianie kontroli nad dźwiękiem, jak opisano w artykule Zarządzanie kontrolą nad dźwiękiem. 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, pamiętaj, że tylko aktywne okno będzie wyświetlane w trybie obrazu w obrazie. W niektórych sytuacjach, np. na urządzeniach z wielofunkcyjnymi oknami, możliwe jest, że aktywność poniżej będzie teraz wyświetlana i będzie widoczna ponownie obok PiP. W takim przypadku należy odpowiednio zająć się sprawą, w tym onResume() lub onPause(). Możliwe też, że użytkownik wejdzie w interakcję z treścią. Jeśli np. wyświetlana jest aktywność listy filmów, a w tle odtwarzany jest film w trybie obrazu w obrazie, użytkownik może wybrać nowy film z listy, a aktywność obrazu w obrazie powinna się odpowiednio zaktualizować.

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