A partire da Android 8.0 (livello API 26), Android consente l'avvio delle attività in modalità Picture in picture (PIP). Il PIP è un tipo speciale di modalità multi-finestra utilizzato principalmente per la riproduzione video. Consente all'utente di guardare un video in una piccola finestra bloccata in un angolo dello schermo mentre naviga tra le app o sfoglia i contenuti nella schermata principale.
La funzionalità PIP sfrutta le API multi-finestra disponibili in Android 7.0 per fornire la finestra di overlay video bloccata. Per aggiungere la modalità PIP alla tua app, devi registrare le tue attività che supportano la modalità PIP, passare alla modalità PIP in base alle necessità e assicurarti che gli elementi dell'interfaccia utente siano nascosti e che la riproduzione video continui quando l'attività è in modalità PIP.
La finestra PIP viene visualizzata nel livello più alto dello schermo, in un angolo scelto dal sistema.
La funzionalità PIP è supportata anche sui dispositivi Android TV OS compatibili con Android 14 (livello API 34) o versioni successive. Sebbene ci siano molte somiglianze, ci sono ulteriori considerazioni da fare quando si utilizza PIP sulla TV.
In che modo gli utenti possono interagire con la finestra PIP
Gli utenti possono trascinare la finestra PIP in un'altra posizione. A partire da Android 12, gli utenti possono anche:
Tocca una volta la finestra per visualizzare un pulsante di attivazione/disattivazione a schermo intero, un pulsante di chiusura, un pulsante delle impostazioni e azioni personalizzate fornite dall'app (ad esempio, controlli di riproduzione).
Tocca due volte la finestra per alternare le dimensioni correnti del PIP e quelle massime o minime del PIP. Ad esempio, se tocchi due volte una finestra ingrandita, questa viene ridotta a icona e viceversa.
Nascondi la finestra trascinandola sul bordo sinistro o destro. Per ripristinare la finestra, tocca la parte visibile della finestra nascosta o trascinala fuori.
Ridimensiona la finestra PIP utilizzando il gesto di pizzicare per zoomare.
La tua app controlla quando l'attività corrente entra in modalità PIP. Ecco alcuni esempi:
Un'attività può entrare in modalità PIP quando l'utente tocca il pulsante Home o scorre verso l'alto fino alla schermata Home. In questo modo, Google Maps continua a mostrare le indicazioni stradali mentre l'utente svolge un'altra attività contemporaneamente.
La tua app può spostare un video in modalità PIP quando l'utente torna indietro dal video per sfogliare altri contenuti.
La tua app può passare alla modalità PIP mentre un utente guarda la fine di un episodio di contenuti. La schermata principale mostra informazioni promozionali o riassuntive sul prossimo episodio della serie.
La tua app può offrire agli utenti un modo per mettere in coda altri contenuti mentre guardano un video. Il video continua a essere riprodotto in modalità PIP mentre la schermata principale mostra un'attività di selezione dei contenuti.
Dichiarare il supporto PIP
Per impostazione predefinita, il sistema non supporta automaticamente la modalità PIP per le app. Se vuoi
supportare la modalità PIP nella tua app, registra la tua attività video nel manifest impostando
android:supportsPictureInPicture
su true
. Inoltre, specifica che l'attività gestisce le modifiche alla configurazione del layout in modo che non venga riavviata quando si verificano modifiche al layout durante le transizioni della modalità PIP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Passare all'attività in PIP
A partire da Android 12, puoi passare alla modalità PIP impostando
il flag setAutoEnterEnabled
su true
. Con questa impostazione, un'attività passa automaticamente alla modalità PIP in base alle necessità senza dover chiamare esplicitamente enterPictureInPictureMode()
in onUserLeaveHint
. Inoltre, questo offre il vantaggio
di transizioni molto più fluide. Per maggiori dettagli, vedi Rendere
più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti.
Se scegli come target Android 11 o versioni precedenti, un'attività deve chiamare
enterPictureInPictureMode()
per passare alla modalità PIP. Ad esempio, il seguente codice passa un'attività alla
modalità PIP quando l'utente fa clic su un pulsante dedicato nell'interfaccia utente dell'app:
override fun onActionClicked(action: Action) { if (action.id.toInt() == R.id.lb_control_picture_in_picture) { activity?.enterPictureInPictureMode() return } }
@Override public void onActionClicked(Action action) { if (action.getId() == R.id.lb_control_picture_in_picture) { getActivity().enterPictureInPictureMode(); return; } ... }
Potresti voler includere una logica che attiva la modalità PIP per un'attività
anziché metterla in background. Ad esempio, Google Maps passa alla modalità PIP se
l'utente preme il pulsante Home o Recenti mentre l'app è in navigazione. Puoi
risolvere questo problema eseguendo l'override
di onUserLeaveHint()
:
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Consigliato: offri agli utenti un'esperienza di transizione PIP ottimizzata
Android 12 ha aggiunto miglioramenti estetici significativi alle transizioni animate tra le finestre a schermo intero e PiP. Ti consigliamo vivamente di implementare tutte le modifiche applicabili. Una volta fatto, queste modifiche vengono scalate automaticamente su schermi di grandi dimensioni come pieghevoli e tablet senza ulteriori interventi necessari.
Se la tua app non include gli aggiornamenti applicabili, le transizioni PIP sono ancora funzionali, ma le animazioni sono meno raffinate. Ad esempio, il passaggio dalla modalità a schermo intero alla modalità PIP può causare la scomparsa della finestra PIP durante la transizione prima che riappaia al termine della transizione.
Queste modifiche riguardano quanto segue.
- Rendere più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti
- Impostazione di un
sourceRectHint
corretto per l'ingresso e l'uscita dalla modalità PIP - Disattivazione del ridimensionamento continuo per i contenuti non video
Consulta l'esempio di Android Kotlin PictureInPicture come riferimento per attivare un'esperienza di transizione ottimizzata.
Rendere più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti
A partire da Android 12, il flag setAutoEnterEnabled
offre un'animazione molto più fluida per la transizione ai contenuti video in modalità PIP utilizzando la navigazione tramite gesti, ad esempio quando scorri verso l'alto per tornare alla schermata Home dalla modalità a schermo intero.
Per apportare questa modifica, completa i seguenti passaggi e consulta questo esempio come riferimento:
Utilizza
setAutoEnterEnabled
per crearePictureInPictureParams.Builder
:setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Chiama
setPictureInPictureParams
con l'PictureInPictureParams
aggiornato in anticipo. L'app non attende il callbackonUserLeaveHint
(come avrebbe fatto in Android 11).Ad esempio, potresti voler chiamare
setPictureInPictureParams
alla prima riproduzione e a quelle successive se il formato viene modificato.Chiama
setAutoEnterEnabled(false)
, ma solo se necessario. Ad esempio, probabilmente non vuoi attivare la modalità PIP se la riproduzione corrente è in pausa.
Imposta un sourceRectHint
appropriato per entrare e uscire dalla modalità PIP
A partire dall'introduzione del Picture in picture in Android 8.0, setSourceRectHint
indicava l'area dell'attività visibile dopo la transizione in
picture in picture, ad esempio i limiti della visualizzazione del video in un video player.
Con Android 12, il sistema utilizza sourceRectHint
per implementare un'animazione molto più fluida
sia quando si entra che quando si esce dalla modalità PIP.
Per impostare correttamente sourceRectHint
per l'attivazione e la disattivazione della modalità PIP:
Costruisci
PictureInPictureParams
utilizzando i limiti corretti comesourceRectHint
. Ti consigliamo inoltre di collegare un listener di modifica del layout al video player: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)
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);
Se necessario, aggiorna
sourceRectHint
prima che il sistema inizi la transizione di uscita. Quando il sistema sta per uscire dalla modalità PIP, la gerarchia di visualizzazione dell'attività viene disposta nella configurazione di destinazione (ad esempio, a schermo intero). L'app può collegare un listener di modifica del layout alla visualizzazione principale o alla visualizzazione di destinazione (ad esempio la visualizzazione del video player) per rilevare l'evento e aggiornaresourceRectHint
prima dell'inizio dell'animazione.// 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() ) } }
// 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()); } });
Disattivare il ridimensionamento continuo per i contenuti non video
Android 12 aggiunge il flag setSeamlessResizeEnabled
, che fornisce un'animazione di dissolvenza incrociata molto
più fluida quando si ridimensionano i contenuti non video nella finestra
PIP. In precedenza, il ridimensionamento dei contenuti non video in una finestra PIP poteva creare
artefatti visivi stridenti.
Per attivare il ridimensionamento continuo dei contenuti video:
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());
Gestire l'interfaccia utente durante la modalità PIP
Quando l'attività entra o esce dalla modalità Picture in picture (PIP), il sistema chiama Activity.onPictureInPictureModeChanged()
o Fragment.onPictureInPictureModeChanged()
.
Android 15 introduce modifiche che garantiscono una transizione ancora più fluida quando si entra in modalità PIP. Ciò è utile per le app che hanno elementi UI sovrapposti alla UI principale, che passa alla modalità PIP.
Gli sviluppatori utilizzano il callback onPictureInPictureModeChanged()
per definire la logica che attiva/disattiva la visibilità degli elementi dell'interfaccia utente sovrapposti.
Questo callback viene attivato al termine dell'animazione di entrata o uscita dalla modalità PIP.
A partire da Android 15, la classe PictureInPictureUiState
include un nuovo stato.
Con questo nuovo stato della UI, le app che hanno come target Android 15 osservano l'invocazione del callback Activity#onPictureInPictureUiStateChanged()
con isTransitioningToPip()
non appena inizia l'animazione PiP.
Esistono molti elementi dell'interfaccia utente che non sono pertinenti per l'app quando è in modalità PIP,
ad esempio visualizzazioni o layout che includono informazioni come suggerimenti, video
in arrivo, valutazioni e titoli. Quando l'app passa alla modalità PIP, utilizza il callback onPictureInPictureUiStateChanged()
per nascondere questi elementi dell'interfaccia utente. Quando l'app passa alla modalità a schermo intero dalla finestra PiP, utilizza il callback onPictureInPictureModeChanged()
per mostrare di nuovo questi elementi, come mostrato negli esempi seguenti:
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Questo rapido toggle di visibilità degli elementi della UI irrilevanti (per una finestra PIP) contribuisce a garantire un'animazione di ingresso PIP più fluida e senza sfarfallio.
Esegui l'override di questi callback per ridisegnare gli elementi UI dell'attività. Tieni presente che, in modalità PIP, la tua attività viene mostrata in una piccola finestra. Gli utenti non possono interagire con gli elementi dell'interfaccia utente della tua app quando l'app è in modalità PIP e i dettagli degli elementi dell'interfaccia utente di piccole dimensioni potrebbero essere difficili da vedere. Le attività di riproduzione video con un'interfaccia utente minimale offrono la migliore esperienza utente.
Se la tua app deve fornire azioni personalizzate per la modalità PIP, vedi Aggiungere controlli in questa pagina. Rimuovi gli altri elementi dell'interfaccia utente prima che l'attività entri in modalità PIP e ripristinali quando l'attività torna a schermo intero.
Aggiungi controlli
La finestra PIP può mostrare i controlli quando l'utente apre il menu della finestra (toccando la finestra su un dispositivo mobile o selezionando il menu dal telecomando della TV).
Se un'app ha una sessione multimediale attiva, verranno visualizzati i controlli di riproduzione, pausa, successivo e precedente.
Puoi anche specificare azioni personalizzate in modo esplicito creando
PictureInPictureParams
con
PictureInPictureParams.Builder.setActions()
prima di entrare in modalità PIP e passare i parametri quando entri in modalità PIP utilizzando
enterPictureInPictureMode(android.app.PictureInPictureParams)
o
setPictureInPictureParams(android.app.PictureInPictureParams)
.
Fai attenzione. Se provi ad aggiungere più di
getMaxNumPictureInPictureActions()
,
riceverai solo il numero massimo.
Continuare la riproduzione del video in modalità PIP
Quando l'attività passa alla modalità PIP, il sistema la mette in stato di pausa e chiama il metodo onPause()
dell'attività. La riproduzione
video non deve essere messa in pausa e deve continuare se l'attività viene
sospesa durante la transizione alla modalità PIP.
In Android 7.0 e versioni successive, devi mettere in pausa e riprendere la riproduzione video quando il
sistema chiama l'attività
onStop()
e
onStart()
. In questo modo,
non dovrai controllare se l'app è in modalità PIP in onPause()
e
continuare esplicitamente la riproduzione.
Se non hai impostato il flag setAutoEnterEnabled
su true
e devi
mettere in pausa la riproduzione nell'implementazione di onPause()
, controlla la modalità PIP chiamando
isInPictureInPictureMode()
e gestisci la riproduzione in modo appropriato. Ad esempio:
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. } }
@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. ... } }
Quando l'attività esce dalla modalità PIP e torna alla modalità a schermo intero, il sistema
riprende l'attività e chiama il metodo
onResume()
.
Utilizzare una singola attività di riproduzione per la modalità PIP
Nella tua app, un utente potrebbe selezionare un nuovo video mentre cerca contenuti nella schermata principale, mentre un'attività di riproduzione video è in modalità PIP. Riproduci il nuovo video nell'attività di riproduzione esistente in modalità a schermo intero, anziché avviare una nuova attività che potrebbe confondere l'utente.
Per garantire che venga utilizzata una sola attività per le richieste di riproduzione video e per passare
alla modalità PIP o uscire da questa modalità in base alle necessità, imposta android:launchMode
dell'attività su
singleTask
nel manifest:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
Nella tua attività, esegui l'override
onNewIntent()
e gestisci il nuovo video, interrompendo la riproduzione di qualsiasi video esistente, se necessario.
Best practice
La funzionalità PIP potrebbe essere disattivata sui dispositivi con poca RAM. Prima che la tua app utilizzi la funzionalità PIP,
verifica che sia disponibile chiamando
hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
La modalità PIP è pensata per le attività che riproducono video a schermo intero. Quando passi all'attività in modalità PIP, evita di mostrare qualsiasi cosa tranne i contenuti video. Tieni traccia di quando la tua attività entra in modalità PIP e nascondi gli elementi dell'interfaccia utente, come descritto in Gestione dell'interfaccia utente durante la modalità PIP.
Quando un'attività è in modalità PIP, per impostazione predefinita non riceve lo stato attivo. Per
ricevere eventi di input in modalità PIP, utilizza
MediaSession.setCallback()
.
Per saperne di più sull'utilizzo di setCallback()
, vedi Visualizzare una scheda Now Playing.
Quando l'app è in modalità PIP, la riproduzione video nella finestra PIP può causare interferenze audio con un'altra app, ad esempio un'app di riproduzione musicale o di ricerca vocale. Per evitare questo problema, richiedi l'audio focus quando inizi a riprodurre il video e gestisci le notifiche di modifica dell'audio focus, come descritto in Gestione dell'audio focus. Se ricevi una notifica di perdita della messa a fuoco audio in modalità PIP, metti in pausa o interrompi la riproduzione del video.
Quando l'app sta per entrare in modalità PIP, tieni presente che solo l'attività in primo piano entra
in modalità Picture in picture. In alcune situazioni, ad esempio sui dispositivi multi-finestra, è
possibile che l'attività sottostante venga ora mostrata e diventi di nuovo visibile insieme
all'attività PIP. Devi gestire questa richiesta di assistenza di conseguenza, incluso il
ricevimento di un callback onResume()
o onPause()
per l'attività riportata di seguito. È anche
possibile che l'utente interagisca con l'attività. Ad esempio, se hai
un'attività di elenco di video visualizzata e l'attività di riproduzione video in modalità PIP, l'utente potrebbe selezionare un nuovo video dall'elenco e l'attività PIP dovrebbe aggiornarsi di conseguenza.
Codice campione aggiuntivo
Per scaricare un'app di esempio scritta in Kotlin, consulta Android PictureInPicture Sample (Kotlin).