A partir de Android 8.0 (nivel de API 26), Android permite que las actividades se inicien en el modo de pantalla en pantalla (PIP). PIP es un tipo especial de modo multiventana que se usa principalmente para la reproducción de videos. Permite al usuario ver un video en una ventana pequeña fijada en una esquina de la pantalla mientras navega entre apps o explora contenido en la pantalla principal.
PIP aprovecha las API de multiventana disponibles en Android 7.0 para proporcionar la ventana de video fijada superpuesta. Para agregar PIP a tu app, debes registrar las actividades que admitan PIP, cambiar tu actividad al modo de PIP según sea necesario y asegurarte de que los elementos de la IU estén ocultos y la reproducción de video continúe cuando la actividad esté en modo de PIP.
La ventana de PiP aparece en la capa superior de la pantalla, en una esquina que elige el sistema.
PIP también es compatible con dispositivos con el SO Android TV compatibles que ejecutan Android 14 (nivel de API 34) o versiones posteriores. Si bien hay muchas similitudes, existen consideraciones adicionales cuando se usa la función PiP en la TV.
Cómo pueden interactuar los usuarios con la ventana de PIP
Los usuarios pueden arrastrar la ventana de PIP a otra ubicación. A partir de Android 12, los usuarios también pueden hacer lo siguiente:
Presionar una vez la ventana para mostrar un botón de activación de pantalla completa, un botón de cierre, un botón de configuración y acciones personalizadas que proporciona tu app (por ejemplo, controles de reproducción).
Presiona dos veces la ventana para cambiar entre el tamaño actual del PiP y el máximo o mínimo. Por ejemplo, si presionas dos veces una ventana maximizada, se minimiza, y lo contrario también es cierto.
Para ocultar la ventana, arrástrala hacia el borde izquierdo o derecho. Para dejar de almacenar la ventana, presiona la parte visible de la ventana almacenada o arrástrala hacia afuera.
Cambiar el tamaño de la ventana de PIP con la función de pellizcar para hacerle zoom.
Tu app controla cuándo ingresa en modo de PIP la actividad actual. Estos son algunos ejemplos:
Una actividad puede ingresar al modo de PIP cuando el usuario presiona el botón de inicio o desliza el dedo hacia arriba para ir a la pantalla principal. Así es como Google Maps sigue mostrando instrucciones sobre cómo llegar mientras el usuario ejecuta otra actividad al mismo tiempo.
Tu app puede mover un video al modo de PIP cuando el usuario navega hacia atrás desde el video para explorar otro contenido.
Tu app puede cambiar un video al modo de PIP mientras un usuario mira el final de un episodio de contenido. La pantalla principal muestra información promocional o de resumen sobre el próximo episodio de la serie.
Tu app puede proporcionar una manera para que los usuarios pongan en cola contenido adicional mientras miran un video. El video sigue reproduciéndose en modo de PIP mientras la pantalla principal muestra una actividad de selección de contenido.
Cómo declarar la compatibilidad con PIP
De forma predeterminada, el sistema no admite automáticamente PIP para apps. Si deseas admitir este modo en tu app, configura android:supportsPictureInPicture
en true
para registrar la actividad de video en tu manifiesto. Además, especifica que tu actividad controla los cambios de configuración de diseño para que no se reinicie cuando se produzcan cambios de diseño durante las transiciones del modo de PIP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Cómo cambiar tu actividad al modo PiP
A partir de Android 12, puedes cambiar tu actividad al modo de PIP configurando la marca setAutoEnterEnabled
en true
. Con este parámetro de configuración, una actividad cambia automáticamente al modo de PIP según sea necesario sin tener que llamar explícitamente a enterPictureInPictureMode()
en onUserLeaveHint
. Además, esto tiene el beneficio adicional de proporcionar transiciones mucho más fluidas. Para obtener más información, consulta Cómo lograr que las transiciones al modo de PIP sean más fluidas desde la navegación por gestos.
Si te orientas a Android 11 o versiones anteriores, una actividad debe llamar a enterPictureInPictureMode()
para cambiar al modo PiP. Por ejemplo, en el siguiente código, se cambia una actividad al modo de PIP cuando el usuario hace clic en un botón exclusivo en la IU de la 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; } ... }
Es posible que desees incluir lógica que cambie una actividad al modo de PIP en lugar de pasar a segundo plano. Por ejemplo, Google Maps cambia al modo de PIP si el usuario presiona el botón Recientes o de inicio mientras la app está navegando. Para solucionar este caso, anula onUserLeaveHint()
:
Kotlin
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Java
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Opción recomendada: Ofrece a los usuarios una experiencia de transición de PiP optimizada
En Android 12, se agregaron mejoras estéticas significativas a las transiciones animadas entre las ventanas de pantalla completa y PiP. Te recomendamos que implementes todos los cambios aplicables. Una vez que lo hagas, estos cambios se escalarán automáticamente a pantallas grandes, como plegables y tablets, sin necesidad de realizar más trabajo.
Si tu app no incluye actualizaciones aplicables, las transiciones de PIP seguirán siendo funcionales, pero las animaciones estarán menos pulidas. Por ejemplo, la transición de pantalla completa al modo de PIP puede hacer que la ventana de PIP desaparezca durante la transición antes de volver a aparecer cuando se complete.
Estos cambios incluyen lo siguiente:
- Cómo lograr que las transiciones al modo de PIP sean más fluidas desde la navegación por gestos
- Configurar un
sourceRectHint
adecuado para entrar al modo de PIP y salir de él - Cómo inhabilitar el cambio fluido de tamaño de contenido que no sea de video
Consulta la muestra de PictureInPicture de Android Kotlin como referencia para habilitar una experiencia de transición pulida.
Cómo lograr que las transiciones al modo de PIP sean más fluidas desde la navegación por gestos
A partir de Android 12, la marca setAutoEnterEnabled
proporciona una animación mucho más fluida para la transición al contenido de video en el modo de PIP con la navegación por gestos, por ejemplo, cuando se desliza el dedo hacia arriba para ir a la pantalla principal desde la pantalla completa.
Completa los siguientes pasos para realizar este cambio y consulta esta muestra como referencia:
Usa
setAutoEnterEnabled
para construirPictureInPictureParams.Builder
:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Llama a
setPictureInPictureParams
con la clasePictureInPictureParams
actualizada con anticipación. La app no espera la devolución de llamadaonUserLeaveHint
(como sí habría sucedido con Android 11).Por ejemplo, es posible que desees llamar a
setPictureInPictureParams
en la primera reproducción y en cualquier reproducción posterior si se cambia la relación de aspecto.Llama a
setAutoEnterEnabled(false)
, pero solo cuando sea necesario. Por ejemplo, es probable que no quieras ingresar PIP si la reproducción actual se encuentra en estado de pausa.
Establece un sourceRectHint
adecuado para ingresar y salir del modo PiP
A partir de la introducción de PIP en Android 8.0, setSourceRectHint
indicó el área de la actividad que es visible después de la transición a pantalla en pantalla (por ejemplo, los límites de la vista de video en un reproductor de video).
Con Android 12, el sistema usa sourceRectHint
para implementar una animación mucho más fluida cuando se ingresa y se sale del modo de PIP.
Para configurar correctamente sourceRectHint
para ingresar y salir del modo PiP, haz lo siguiente:
Construye
PictureInPictureParams
con los límites adecuados comosourceRectHint
. También te recomendamos que adjuntes un objeto de escucha de cambios de diseño al reproductor de video: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);
Si es necesario, actualiza la marca
sourceRectHint
antes de que el sistema inicie la transición de salida. Cuando el sistema está a punto de salir del modo de PIP, la jerarquía de vistas de la actividad se presenta en su configuración de destino (por ejemplo, pantalla completa). La app puede adjuntar un objeto de escucha de cambios de diseño a su vista raíz o de destino (como la vista del reproductor de video) para detectar el evento y actualizar elsourceRectHint
antes de que comience la animación.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()); } });
Cómo inhabilitar el cambio fluido de tamaño de contenido que no sea video
En Android 12, se agrega la marca setSeamlessResizeEnabled
, que brinda una animación entre difuminados mucho más fluida cuando se cambia el tamaño de contenido que no es video en la ventana de PiP. Anteriormente, si se cambiaba el tamaño de este tipo de contenido en una ventana de PiP, se podían crear artefactos visuales molestos.
Para inhabilitar el cambio fluido de tamaño de contenido que no sea video, usa el siguiente fragmento de código:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(false) .build());
Cómo controlar la IU durante el modo PiP
Cuando la actividad ingresa al modo de pantalla en pantalla (PiP) o sale de este, el sistema llama a Activity.onPictureInPictureModeChanged()
o a Fragment.onPictureInPictureModeChanged()
.
Android 15 introduce cambios que garantizan una transición aún más fluida cuando se ingresa al modo de PIP. Esto es beneficioso para las apps que tienen elementos de IU superpuestos sobre su IU principal, que se muestra en PiP.
Los desarrolladores usan la devolución de llamada onPictureInPictureModeChanged()
para definir la lógica que activa o desactiva la visibilidad de los elementos superpuestos de la IU.
Esta devolución de llamada se activa cuando se completa la animación de entrada o salida del PiP.
A partir de Android 15, la clase PictureInPictureUiState
incluye un estado nuevo.
Con este nuevo estado de la IU, las apps orientadas a Android 15 observan que se invoca la devolución de llamada Activity#onPictureInPictureUiStateChanged()
con isTransitioningToPip()
tan pronto como comienza la animación de PIP.
Hay muchos elementos de la IU que no son relevantes para la app cuando está en modo PiP, por ejemplo, vistas o diseños que incluyen información como sugerencias, videos próximos, calificaciones y títulos. Cuando la app entre en el modo de PIP, usa la devolución de llamada onPictureInPictureUiStateChanged()
para ocultar estos elementos de la IU. Cuando la app cambia al modo de pantalla completa desde la ventana de PiP, usa la devolución de llamada onPictureInPictureModeChanged()
para mostrar estos elementos, como se muestra en los siguientes ejemplos:
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. } }
Este botón de activación rápida de visibilidad de elementos irrelevantes de la IU (para una ventana de PIP) ayuda a garantizar una animación de entrada de PIP más fluida y sin parpadeos.
Anula estas devoluciones de llamada para volver a dibujar los elementos de la IU de la actividad. Ten en cuenta que, en el modo de PIP, la actividad se muestra en una ventana pequeña. Los usuarios no pueden interactuar con los elementos de la IU de la app cuando está en modo de PIP, y los detalles de los elementos pequeños de la IU pueden ser difíciles de ver. Las actividades de reproducción de video con una IU mínima proporcionan la mejor experiencia del usuario.
Si la app necesita proporcionar acciones personalizadas para el modo de PIP, consulta Cómo agregar controles en esta página. Quita otros elementos de la IU antes de que la actividad ingrese a PIP y restablécelos cuando vuelva a ser de pantalla completa.
Agregar controles
La ventana de PIP puede mostrar controles cuando el usuario abre el menú de la ventana (tocando la ventana en un dispositivo móvil o seleccionando el menú desde el control remoto de la TV).
Si una app tiene una sesión multimedia activa, aparecerán los controles de reproducción, pausa, siguiente y anterior.
También puedes especificar acciones personalizadas de manera explícita. Para ello, compila PictureInPictureParams
con PictureInPictureParams.Builder.setActions()
antes de ingresar al modo de PIP y pasa los parámetros cuando ingreses al modo de PIP con enterPictureInPictureMode(android.app.PictureInPictureParams)
o setPictureInPictureParams(android.app.PictureInPictureParams)
.
Cuidado. Si intentas agregar más de getMaxNumPictureInPictureActions()
, solo obtendrás el número máximo.
Cómo continuar con la reproducción de video en PiP
Cuando tu actividad cambia a PIP, el sistema coloca la actividad en estado de pausa y llama al método onPause()
de la actividad. La reproducción de video no debe pausarse y, en su lugar, continuar reproduciéndose si se pausa la actividad durante la transición al modo de PIP.
En Android 7.0 y versiones posteriores, debes pausar y reanudar la reproducción de video cuando el sistema llame a los elementos onStop()
y onStart()
de tu actividad. De esta manera, puedes evitar tener que verificar si la app está en modo de PIP en onPause()
y continuar explícitamente la reproducción.
Si no configuraste la marca setAutoEnterEnabled
en true
y necesitas pausar la reproducción en tu implementación de onPause()
, llama a isInPictureInPictureMode()
para verificar el modo de PIP y controla la reproducción de manera adecuada. Por ejemplo:
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. ... } }
Cuando tu actividad cambia del modo de PIP al modo de pantalla completa, el sistema reanuda tu actividad y llama al método onResume()
.
Cómo usar una sola actividad de reproducción para el modo PIP
En tu app, un usuario puede seleccionar un video nuevo cuando busca contenido en la pantalla principal mientras una actividad de reproducción de video está en modo de PIP. Reproduce el video nuevo en la actividad de reproducción existente en modo de pantalla completa en lugar de iniciar una actividad nueva que podría confundir al usuario.
Para garantizar que se use una sola actividad para las solicitudes de reproducción de video y se cambie al modo de PiP o se salga de él según sea necesario, configura android:launchMode
en singleTask
en tu manifiesto:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
En la actividad, anula onNewIntent()
y controla el video nuevo, lo cual detiene cualquier reproducción de video existente si es necesario.
Recomendaciones
El modo de PIP puede estar inhabilitado en dispositivos que tienen poca RAM. Antes de que tu app use PIP, verifica que esté disponible llamando a hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
.
El modo de PIP está destinado a actividades que reproducen video en pantalla completa. Cuando cambies tu actividad al modo de PIP, evita mostrar algo que no sea contenido de video. Realiza un seguimiento de cuándo tu actividad ingresa al modo de PIP y oculta los elementos de la IU, como se describe en Cómo controlar las IU durante el modo de PIP.
Cuando una actividad está en modo de PIP, de forma predeterminada, no recibe el foco de entrada. Para recibir eventos de entrada mientras se está usando el modo de PIP, usa MediaSession.setCallback()
.
Para obtener más información sobre el uso de setCallback()
, consulta Cómo mostrar una tarjeta de Está sonando.
Cuando tu app está en modo de PIP, la reproducción de video en la ventana de PIP puede causar interferencias de audio con otra app, como una app de reproductor de música o de búsqueda por voz. Para evitar esto, solicita el foco de audio cuando comiences a reproducir el video y controla las notificaciones de cambio de foco de audio, como se describe en Cómo administrar el foco de audio. Si recibes una notificación de pérdida de foco de audio cuando se usa el modo de PIP, pausa o detén la reproducción de video.
Cuando se esté por activar PIP en tu app, ten en cuenta que solo la actividad principal ingresará al modo de pantalla en pantalla. En algunos casos, como en los dispositivos multiventana, es posible que la actividad menor se muestre y se vuelva a ver junto con la actividad en modo de PIP. Debes controlar esta situación como corresponde, lo cual incluye proporcionar una devolución de llamada onResume()
o onPause()
a la actividad menor. También es posible que el usuario interactúe con la actividad. Por ejemplo, si se mostrara una actividad de lista de videos y la actividad de reproducción de video estuviera en modo de PIP, el usuario podría seleccionar un video nuevo de la lista y la actividad en modo de PIP debería actualizarse en consecuencia.
Código de ejemplo adicional
Para descargar una app de ejemplo escrita en Kotlin, consulta Muestra de Android PictureInPicture (Kotlin).