A partire da Android 8.0 (livello API 26), Android consente l'avvio delle attività in modalità Picture in picture (PIP). La modalità PiP è un tipo speciale di modalità multi-finestra utilizzata principalmente per la riproduzione di 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 sulla schermata principale.
PIP sfrutta le API multi-finestra rese disponibili in Android 7.0 per fornire la finestra overlay video fissata. Per aggiungere la modalità PiP alla tua app, devi registrare le attività che la supportano, passare alla modalità PiP se necessario e assicurarti che gli elementi dell'interfaccia utente siano nascosti e che la riproduzione dei 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 modalità PiP è supportata anche sui dispositivi con sistema operativo Android TV compatibili con Android 14 (livello API 34) o versioni successive. Sebbene esistano molte analogie, ci sono ulteriori considerazioni 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'opzione di attivazione/disattivazione a schermo intero, un pulsante di chiusura, un pulsante di impostazioni e azioni personalizzate fornite dall'app (ad esempio i controlli di riproduzione).
Tocca due volte la finestra per passare dalla dimensione PIP attuale a quella massima o minima in PIP. Ad esempio, se tocchi due volte una finestra ingrandita la dimensione PIP viene ridotta al minimo. Viene applicato anche il contrario.
Nascondi la finestra trascinandola sul lato sinistro o destro. Per rimuovere la finestra dall'elenco filtri, tocca la parte visibile della finestra nascosta o trascinala fuori.
Ridimensiona la finestra PiP utilizzando la funzionalità pizzica per regolare lo zoom.
L'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 scorri verso l'alto fino alla schermata Home. In questo modo, Google Maps continua a mostrare le indicazioni stradali mentre l'utente esegue contemporaneamente un'altra attività.
La tua app può spostare un video in modalità PIP quando l'utente torna dal video per sfogliare altri contenuti.
La tua app può passare un video in modalità PiP mentre un utente guarda la fine di un episodio di contenuti. Nella schermata principale vengono visualizzate informazioni promozionali o di riepilogo sull'episodio successivo 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 sullo schermo principale viene visualizzata un'attività di selezione dei contenuti.
Dichiara il supporto dei PIP
Per impostazione predefinita, il sistema non supporta automaticamente il PiP per le app. Se vuoi supportare PiP nella tua app, registra la tua attività video nel file manifest impostando android:supportsPictureInPicture
su true
. Inoltre, specifica che la tua attività gestisce le modifiche alla configurazione del layout in modo che non venga riavviata quando vengono apportate modifiche al layout durante le transizioni dalla modalità PIP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Passa alla modalità PIP della tua attività
A partire da Android 12, puoi passare la tua attività alla modalità PiP impostando il flag setAutoEnterEnabled
su true
. Con questa impostazione, un'attività passa automaticamente alla modalità PiP in base alle esigenze senza dover chiamare esplicitamente enterPictureInPictureMode()
in onUserLeaveHint
. Questo ha l'ulteriore vantaggio di offrire
transizioni molto più fluide. Per maggiori dettagli, vedi Rendere più agevoli le transizioni alla modalità PiP dalla navigazione con i 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 imposta un'attività sulla modalità PiP quando l'utente fa clic su un pulsante dedicato nell'interfaccia utente dell'app:
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; } ... }
Potresti includere una logica che inserisca un'attività in modalità PiP anziché 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 gestire questo caso eseguendo l'override di onUserLeaveHint()
:
Kotlin
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Java
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Consigliato: offri agli utenti un'esperienza di transizione PIP migliorata
Android 12 ha apportato notevoli miglioramenti estetici alle transizioni animate tra le finestre a schermo intero e PiP. Ti consigliamo vivamente di implementare tutte le modifiche applicabili. Una volta apportate, queste modifiche verranno adattate automaticamente agli schermi di grandi dimensioni, come i dispositivi pieghevoli e i tablet, senza ulteriori interventi.
Se la tua app non include aggiornamenti applicabili, le transizioni PiP sono ancora funzionali, ma le animazioni sono meno raffinate. Ad esempio, la transizione 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.
- Miglioramento delle transizioni alla modalità PiP dalla navigazione tramite gesti
- Impostazione di un
sourceRectHint
appropriato per entrare e uscire dalla modalità PiP - Disattivazione del ridimensionamento continuo per i contenuti non video
Fai riferimento all'esempio di Kotlin PictureInPicture per Android come riferimento per consentire un'esperienza di transizione ottimale.
Rendere più fluide le transizioni alla modalità PiP dalla navigazione tramite gesti
A partire da Android 12, il flag setAutoEnterEnabled
offre un'animazione molto fluida per passare ai contenuti video in modalità PIP utilizzando la navigazione tramite gesti, ad esempio quando si scorre verso l'alto dalla modalità a schermo intero.
Per apportare questa modifica, segui questi passaggi e consulta questo esempio come riferimento:
Utilizza
setAutoEnterEnabled
per crearePictureInPictureParams.Builder
:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Chiama
setPictureInPictureParams
con le informazioni aggiornate suPictureInPictureParams
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 qualsiasi riproduzione successiva se le proporzioni vengono modificate.Chiama
setAutoEnterEnabled(false)
, ma solo quando è necessario. Ad esempio, probabilmente non vorrai attivare la modalità PIP se la riproduzione corrente è in stato di pausa.
Impostare 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 al
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 entrare ed uscire dalla modalità PiP:
Costruisci
PictureInPictureParams
utilizzando i limiti appropriati comesourceRectHint
. Consigliamo anche di allegare al video player un listener delle modifiche del layout: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);
Se necessario, aggiorna
sourceRectHint
prima che il sistema avvii la transizione di uscita. Quando il sistema sta per uscire dalla modalità PiP, la gerarchia delle visualizzazioni dell'attività viene organizzata in base alla configurazione di destinazione (ad esempio, schermo intero). L'app può collegare un listener delle modifiche al layout alla vista principale o alla vista di destinazione (ad esempio la vista del video player) per rilevare l'evento e aggiornaresourceRectHint
prima dell'inizio dell'animazione.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()); } });
Disattivare il ridimensionamento automatico per i contenuti non video
Android 12 aggiunge il flag setSeamlessResizeEnabled
, che offre un'animazione di transizione 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 degli artefatti visivi fastidiosi.
Per disattivare il ridimensionamento continuo per i contenuti non video:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build());
Gestire l'interfaccia utente durante il 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 accede alla modalità PiP. Questo è utile per le app che hanno elementi dell'interfaccia utente sovrapposti all'interfaccia utente principale, che passa in PiP.
Gli sviluppatori utilizzano il callback onPictureInPictureModeChanged()
per definire la logica che attiva/disattiva la visibilità degli elementi UI sovrapposti.
Questo callback viene attivato al termine dell'animazione di entrata o uscita in 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 il callback di Activity#onPictureInPictureUiStateChanged()
viene richiamato con isTransitioningToPip()
all'avvio dell'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 programma, 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 nuovamente questi elementi, come mostrato negli esempi seguenti:
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. } }
Questa rapida attivazione/disattivazione della visibilità degli elementi dell'interfaccia utente irrilevanti (per una finestra PiP) contribuisce a garantire un'animazione di entrata PiP più fluida e senza sfarfallio.
Sostituisci questi callback per ridisegnare gli elementi dell'interfaccia utente dell'attività. Tieni presente che, in modalità PIP, la tua attività viene visualizzata in una piccola finestra. Gli utenti non possono interagire con gli elementi UI della tua app quando questa è in modalità PIP e i dettagli di piccoli elementi UI potrebbero essere difficili da vedere. Le attività di riproduzione video con un'interfaccia utente minima offrono la migliore esperienza utente.
Se l'app deve fornire azioni personalizzate per PIP, vedi Aggiungere controlli in questa pagina. Rimuovi gli altri elementi dell'interfaccia utente prima che l'attività entri in PiP e ripristinali quando l'attività torna a essere a schermo intero.
Aggiungi controlli
La finestra PiP può mostrare i controlli quando l'utente apre il menu della finestra (facendo tap sulla finestra su un dispositivo mobile o selezionando il menu dal telecomando della TV).
Se un'app ha una sessione multimediale attiva, vengono visualizzati i controlli di riproduzione, pausa, avanti e precedente.
Puoi anche specificare esplicitamente le azioni personalizzate creando
PictureInPictureParams
con
PictureInPictureParams.Builder.setActions()
prima di attivare la 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.
Riprodurre un video in modalità PiP
Quando l'attività passa a PiP, il sistema la inserisce in stato di pausa e chiama il metodo onPause()
dell'attività. La riproduzione del video non deve essere messa in pausa, ma deve continuare se l'attività viene messa in pausa durante la transizione alla modalità PiP.
In Android 7.0 e versioni successive, dovresti mettere in pausa e riprendere la riproduzione dei video quando il sistema chiama onStop()
e onStart()
della tua attività. In questo modo puoi evitare di dover controllare se la tua app è in modalità PIP in onPause()
e di 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:
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. ... } }
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 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 assicurarti che venga utilizzata una singola attività per le richieste di riproduzione dei video e che la modalità PiP venga attivata o disattivata in base alle esigenze, imposta android:launchMode
su
singleTask
nel file manifest:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
Nella tua attività, sostituisci
onNewIntent()
e gestisci il nuovo video, interrompendo se necessario la riproduzione di eventuali video esistenti.
Best practice
PIP potrebbe essere disattivato sui dispositivi con poca RAM. Prima che la tua app utilizzi 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 attivi la modalità PiP per la tua attività, evita di mostrare altro oltre ai contenuti video. Monitora quando la tua attività entra in modalità PIP e nascondi gli elementi dell'interfaccia utente, come descritto in Gestione dell'interfaccia utente durante PIP.
Quando un'attività è in modalità PIP, per impostazione predefinita non viene attivato l'input. Per ricevere eventi di input in modalità PIP, utilizza MediaSession.setCallback()
.
Per ulteriori informazioni sull'utilizzo di setCallback()
, vedi Visualizzare una scheda Now Playing.
Quando la tua app è in modalità PiP, la riproduzione del video nella finestra PiP può causare interferenze audio con un'altra app, ad esempio un'app di riproduzione di musica o di ricerca vocale. Per evitare questo problema, richiedi l'attenzione audio quando inizi a riprodurre il video e gestisci le notifiche di modifica dell'attenzione audio, come descritto in Gestire l'attenzione audio. Se ricevi la notifica di perdita del focus audio in modalità PIP, metti in pausa o interrompi la riproduzione del video.
Quando l'app sta per entrare in PiP, tieni presente che solo l'attività in primo piano viene visualizzata in modalità PiP. In alcuni casi, ad esempio sui dispositivi con più finestre, è possibile che l'attività riportata di seguito venga visualizzata e torni visibile insieme all'attività PiP. Devi gestire questa richiesta di conseguenza, inclusa l'attività di seguito per ricevere un onResume()
o un onPause()
. È anche possibile che l'utente interagisca con l'attività. Ad esempio, se hai visualizzato un'attività di elenco di video e l'attività di riproduzione di un video in modalità PiP, l'utente potrebbe selezionare un nuovo video dall'elenco e l'attività PiP dovrebbe aggiornarsi di conseguenza.
Codice di esempio aggiuntivo
Per scaricare un'app di esempio scritta in Kotlin, consulta Android PictureInPicture Sample (Kotlin).