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). La pantalla en pantalla es un tipo especial de modo multiventana que se usa para la reproducción de videos, las videollamadas y la navegación. Permite que el usuario fije la ventana de actividad existente 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.
La función de PIP también es compatible con dispositivos con SO Android TV que ejecutan Android 14 (nivel de API 34) o versiones posteriores. Si bien hay muchas similitudes, existen consideraciones adicionales cuando se usa la 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 alternar entre el tamaño actual de PiP y el tamaño máximo o mínimo de PiP. Por ejemplo, si presionas dos veces una ventana maximizada, se minimiza, y viceversa.
Para ocultar la ventana, arrástrala hacia el borde izquierdo o derecho. Para mostrar la ventana, presiona la parte visible de la ventana oculta 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 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 indicaciones mientras el usuario ejecuta otra actividad al mismo tiempo.
Tu app puede mover un video al modo 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 continúa reproduciéndose en el modo de PIP, mientras que 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 quieres admitir el modo de pantalla en pantalla en tu app, registra la actividad de video en el manifiesto estableciendo android:supportsPictureInPicture en true. Además, especifica que tu actividad controla los cambios de configuración del diseño para que no se vuelva a iniciar cuando se produzcan cambios de diseño durante las transiciones del modo pantalla en pantalla.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Implementa PiP con Jetpack
Usa la biblioteca de Jetpack Picture-in-Picture para implementar la experiencia de pantalla en pantalla, ya que optimiza la integración y reduce los problemas comunes en la app. Consulta nuestra app de ejemplo de la plataforma para ver un ejemplo de su uso. Sin embargo, si prefieres implementar el PIP con las APIs de la plataforma, consulta la siguiente documentación.
Cómo cambiar tu actividad a PIP
A partir de Android 12, puedes cambiar tu actividad al modo de pantalla en pantalla configurando la marca setAutoEnterEnabled como 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 de forma explícita a enterPictureInPictureMode() en onUserLeaveHint. Esto tiene el beneficio adicional de proporcionar transiciones mucho más fluidas. Para obtener más detalles, consulta Cómo hacer que las transiciones al modo de PIP sean más fluidas desde la navegación por gestos.
Si tu app se orienta a Android 11 o versiones anteriores, una actividad debe llamar a enterPictureInPictureMode() para cambiar al modo de pantalla en pantalla. Por ejemplo, el siguiente código cambia una actividad al modo de PIP cuando el usuario hace clic en un botón específico de la IU de la app:
override fun onActionClicked(action: Action) {
if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
activity?.enterPictureInPictureMode()
return
}
}
Te recomendamos que incluyas lógica que cambie una actividad al modo de pantalla en pantalla 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. Puedes
detectar este caso anulando onUserLeaveHint():
override fun onUserLeaveHint() {
if (iWantToBeInPipModeNow()) {
enterPictureInPictureMode()
}
}
Recomendación: Proporciona a los usuarios una experiencia de transición de PIP mejorada
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 ajustarán automáticamente a pantallas grandes, como las de dispositivos plegables y tablets, sin necesidad de realizar más trabajo.
Si tu app no incluye las actualizaciones correspondientes, las transiciones de PIP seguirán funcionando, pero las animaciones serán menos fluidas. Por ejemplo, la transición del modo de pantalla completa al modo de PIP puede hacer que la ventana de PIP desaparezca durante la transición antes de que vuelva a aparecer cuando se complete la transición.
Estos cambios implican lo siguiente.
- Transiciones más fluidas al modo de PIP desde la navegación por gestos
- Configura un
sourceRectHintadecuado para entrar y salir del modo de pantalla en pantalla - Inhabilitación del cambio de tamaño sin interrupciones para el contenido que no es de video
Consulta la muestra de PictureInPicture de Kotlin para Android como referencia para habilitar una experiencia de transición mejorada.
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:
- Usa
setAutoEnterEnabledpara construirPictureInPictureParams.Builder:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build())
Note: When `setAutoEnterEnabled` is enabled, you don't need to explicitly
call [`enterPictureInPictureMode`][5] in [`onUserLeaveHint`][6].
Llama a
setPictureInPictureParamscon la clasePictureInPictureParamsactualizada con anticipación. La app no espera la devolución de llamadaonUserLeaveHint(como lo habría hecho en Android 11).Por ejemplo, es posible que quieras llamar a
setPictureInPictureParamsen 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 al modo de PIP si la reproducción actual está en pausa.
Establece un sourceRectHint adecuado para ingresar al modo de pantalla en pantalla y salir de él
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 entra y se sale del modo de PIP.
Para configurar correctamente sourceRectHint para entrar y salir del modo de pantalla en pantalla, haz lo siguiente:
- Crea
PictureInPictureParamscon los límites adecuados comosourceRectHint. También te recomendamos que adjuntes un objeto de escucha de cambio de diseño al reproductor de video:
val mOnLayoutChangeListener =
OnLayoutChangeListener { v: View?, left: Int,
top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop:
Int, oldRight: Int, oldBottom: Int ->
val sourceRectHint = Rect()
mYourVideoView.getGlobalVisibleRect(sourceRectHint)
val builder = PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
setPictureInPictureParams(builder.build())
}
mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
- Si es necesario, actualiza el
sourceRectHintantes de que el sistema inicie la transición de salida. Cuando el sistema está a punto de salir del modo de pantalla en pantalla, 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 cambio de diseño a su vista raíz o de destino (como la vista del reproductor de video) para detectar el evento y actualizar elsourceRectHintantes de que comience la animación.
// 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()
)
}
}
Inhabilitar el cambio de tamaño sin interrupciones para el contenido que no sea de video
Android 12 agrega la marca setSeamlessResizeEnabled, que proporciona una animación de fundido cruzado mucho más fluida cuando se cambia el tamaño del contenido que no es de video en la ventana de PIP. Anteriormente, cambiar el tamaño del contenido que no era de video en una ventana de PIP podía crear
artefactos visuales discordantes.
Para habilitar el cambio de tamaño sin problemas del contenido de video, sigue estos pasos:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(true)
.build())
Cómo controlar la IU durante el modo de PIP
Cuando la actividad entra o sale del modo de pantalla en pantalla (PIP), el sistema llama a Activity.onPictureInPictureModeChanged() o Fragment.onPictureInPictureModeChanged().
Android 15 presenta 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 la IU superpuestos sobre su IU principal, que entra en el modo de pantalla en pantalla.
Los desarrolladores usan la devolución de llamada onPictureInPictureModeChanged() para definir la lógica que activa o desactiva la visibilidad de los elementos de la IU superpuestos. Esta devolución de llamada se activa cuando se completa la animación de entrada o salida de la pantalla en pantalla. A partir de Android 15, la clase PictureInPictureUiState incluye un estado nuevo.
Con este nuevo estado de la IU, las apps que se orientan a Android 15 observan que se invoca la devolución de llamada Activity#onPictureInPictureUiStateChanged() con isTransitioningToPip() en cuanto comienza la animación de PIP. Hay muchos elementos de la IU que no son relevantes para la app cuando está en modo de pantalla en pantalla, por ejemplo, vistas o diseños que incluyen información como sugerencias, próximos videos, calificaciones y títulos. Cuando la app entre en el modo de pantalla en pantalla, usa la devolución de llamada onPictureInPictureUiStateChanged() para ocultar estos elementos de la IU. Cuando la app pasa 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:
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
if (pipState.isTransitioningToPip()) {
// Hide UI elements.
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (!isInPictureInPictureMode) {
// Unhide UI elements.
}
}
Este botón de activación rápida de la visibilidad de los elementos de la IU irrelevantes (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 pantalla en pantalla, tu actividad se muestra en una ventana pequeña. Los usuarios no pueden interactuar con los elementos de la IU de tu app cuando esta se encuentra en el modo de pantalla en pantalla, y puede ser difícil ver los detalles de los elementos pequeños de la IU. Las actividades de reproducción de video con una IU mínima proporcionan la mejor experiencia del usuario.
Si tu app necesita proporcionar acciones personalizadas para la pantalla en pantalla, consulta Cómo agregar controles en esta página. Quita otros elementos de la IU antes de que tu actividad ingrese al modo de PIP y restablécelos cuando la actividad vuelva a 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 forma explícita compilando PictureInPictureParams con PictureInPictureParams.Builder.setActions() antes de ingresar al modo de PIP y pasar los parámetros cuando ingreses al modo de PIP con enterPictureInPictureMode(android.app.PictureInPictureParams) o setPictureInPictureParams(android.app.PictureInPictureParams). Ten cuidado. Si intentas agregar más de getMaxNumPictureInPictureActions(),
solo obtendrás la cantidad máxima.
Cómo continuar la reproducción de video en el modo PIP
Cuando tu actividad cambia a pantalla en pantalla, el sistema la coloca en estado de pausa y llama al método onPause() de la actividad. La reproducción de video no se debe pausar, sino que debe continuar si la actividad se pausa durante la transición al modo de PIP.
En Android 7.0 (nivel de API 24) y versiones posteriores, debes pausar y reanudar la reproducción de video cuando el sistema llame a onStop() y onStart() de tu actividad. De esta manera, puedes evitar tener que verificar si tu app está en el modo de pantalla en pantalla en onPause() y continuar con la reproducción de forma explícita.
Si no configuraste la marca setAutoEnterEnabled como true y necesitas pausar la reproducción en tu implementación de onPause(), llama a isInPictureInPictureMode() para verificar el modo de pantalla en pantalla y controlar la reproducción de forma adecuada. Por ejemplo:
override fun onPause() {
super.onPause()
// If called while in PiP mode, don't pause playback.
if (isInPictureInPictureMode) {
// Continue playback.
} else {
// Use existing playback logic for paused activity behavior.
}
}
Cuando tu actividad sale del modo de pantalla en pantalla y vuelve al modo de pantalla completa, el sistema reanuda la actividad y llama al método onResume().
Usa una sola actividad de reproducción para el modo PIP
En tu app, un usuario puede seleccionar un video nuevo cuando explora contenido en la pantalla principal, mientras una actividad de reproducción de video está en modo PIP. Reproduce el video nuevo en la actividad de reproducción existente en el modo de pantalla completa, en lugar de iniciar una actividad nueva que pueda confundir al usuario.
Para asegurarte de que se use una sola actividad para las solicitudes de reproducción de video y que se cambie al modo de PIP o se salga de él según sea necesario, establece el android:launchMode de la actividad en singleTask en tu manifiesto:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
En tu actividad, anula onNewIntent() y controla el video nuevo, deteniendo cualquier reproducción de video existente si es necesario.
Compatibilidad con PiP para apps de cámara
Para habilitar el modo de PiP en las apps de cámara, debes asegurarte de que la cámara permanezca activa en el modo de PiP sin cerrarla cuando se llame a onPause():
override fun onPause() {
super.onPause()
// Don't close the camera if the app is entering PiP mode
if (!isInPictureInPictureMode) {
closeCamera()
}
}
Al igual que con otros casos de uso, oculta los elementos no esenciales de la IU (como los controles y las superposiciones) y agrega acciones personalizadas para controlar la cámara (por ejemplo, detener la grabación o girar la cámara).
Calcula sourceRectHint para transiciones fluidas
Proporcionar un sourceRectHint preciso con las coordenadas exactas de la pantalla del visor de la cámara es fundamental para una animación de entrada fluida. Puedes obtener los límites de la vista previa con getGlobalVisibleRect() de la siguiente manera:
val previewView = findViewById<View>(R.id.preview_view)
val globalRect = Rect()
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect)
val params = PictureInPictureParams.Builder()
.setSourceRectHint(globalRect)
.build()
setPictureInPictureParams(params)
Consideraciones adicionales
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. Haz 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 la 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 PIP, la reproducción de video en la ventana de PIP puede causar interferencia de audio con otra app, como una app de reproductor de música o de búsqueda por voz. Para evitarlo, 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 del foco de audio en el modo de PIP, pausa o detén la reproducción del video.
Cuando tu app esté por ingresar al modo de PiP, ten en cuenta que solo la actividad superior ingresará a la pantalla en pantalla. En algunas situaciones, como en dispositivos multiventana, es posible que la actividad subyacente ahora se muestre y vuelva a ser visible junto con la actividad de PiP. Debes controlar este caso según corresponda, lo que incluye que la actividad subyacente reciba una devolución de llamada onResume() o onPause().
También es posible que el usuario interactúe con la actividad.
Por ejemplo, si tienes una actividad de lista de videos y la actividad de reproducción de video en el modo de pantalla en pantalla, el usuario podría seleccionar un video nuevo de la lista, y la actividad de pantalla en pantalla debería actualizarse según corresponda.
Código de ejemplo adicional
Para descargar una app de ejemplo escrita en Kotlin, consulta Ejemplo de PictureInPicture para Android (Kotlin).