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). PIP to specjalny typ trybu wielu okien, który jest głównie 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ć 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 to wyświetla się w górnej 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ą korzystać z okna obrazu w obrazie

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.

  • Powiększ lub ściągnij palce, aby zmienić rozmiar okna funkcji obraz w obrazie.

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łączać film w tryb obrazu w pionie, gdy użytkownik ogląda do końca 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. W tym trybie 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 trybu PiP w przypadku aplikacji. 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. 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 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 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 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

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ą:

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

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

  1. Użyj setAutoEnterEnabled, aby utworzyć obiekt PictureInPictureParams.Builder:

    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 pod aktualne informacje o PictureInPictureParams wcześniej. Aplikacja nie czeka na wywołanie zwrotne onUserLeaveHint (tak jak w Androidzie 11).

    Możesz na przykład wywoływać funkcję setPictureInPictureParams przy pierwszym odtworzeniu i kolejnym odtwarzaniu, jeśli zmienisz format obrazu.

  3. Zadzwoń do 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

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 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 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 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ć 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łączanie płynnego dostosowywania rozmiaru w przypadku treści innych niż filmy

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 korzystania z PIP

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 przydatne w przypadku aplikacji, których elementy interfejsu są nałożone na główny interfejs, który przechodzi do obrazu w obrazie.

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 wyzwalane po zakończeniu animacji wejścia lub wyjścia w funkcji obraz w obrazie. W Androidzie 15 klasa PictureInPictureUiState otrzymała nowy stan.

Dzięki temu nowemu stanowi interfejsu aplikacje kierowane na Androida 15 widzą wywołanie zwrotne Activity#onPictureInPictureUiStateChanged() z parametrem isTransitioningToPip() zaraz po rozpoczęciu animacji obrazu w obrazie. 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 obrazu w obrazie, 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 PIP 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. Odtwarzanie filmów przy minimalnym stopniu interfejsu użytkownika zapewnia najlepsze wrażenia użytkownika.

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 Twoja 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 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ż 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ść przejdzie do trybu PiP, system wstrzyma aktywność i wywoła metodę onPause() tej aktywności. Odtwarzanie filmu nie powinno być wstrzymywane. Zamiast tego odtwarzanie jest kontynuowane, jeśli aktywność jest wstrzymana w czasie przejścia w tryb 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 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ż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

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. Monitoruj, gdy aktywność przechodzi w tryb PIP i ukrywa elementy interfejsu zgodnie z opisem w sekcji Obsługa interfejsu w trakcie obrazu w obrazie.

Gdy aktywność jest w trybie obrazu w obrazie, domyślnie nie jest ona skoncentrowana na danych wejściowych. 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 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, zwróć uwagę, że tylko aktywna czynność jest przenoszona do obrazu w obrazie. 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. W takim przypadku należy odpowiednio zająć się tym zgłoszeniem, 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 Kotlin, zobacz Android PictureInPicture Sample (Kotlin).