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

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:

  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 odtworzeniu, gdy współczynnik proporcji ulegnie zmianie.

  3. 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:

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