El modo de ventanas de escritorio permite que los usuarios ejecuten varias apps al mismo tiempo en ventanas de apps redimensionables para una experiencia versátil similar a la de una computadora.
En la figura 1, puedes ver la organización de la pantalla con el modo de ventanas de escritorio habilitado. Información que debes tener en cuenta:
- Los usuarios pueden ejecutar varias apps en paralelo al mismo tiempo.
- La barra de tareas está en una posición fija en la parte inferior de la pantalla y muestra las apps en ejecución. Los usuarios pueden fijar apps para acceder a ellas rápidamente.
- La nueva barra de encabezado personalizable decora la parte superior de cada ventana con controles como minimizar y maximizar.
De forma predeterminada, las apps se abren en pantalla completa en tablets Android. Para iniciar una app en el modo de ventanas de escritorio, mantén presionado el controlador de la ventana en la parte superior de la pantalla y arrástralo dentro de la IU, como se muestra en la figura 2.
Cuando una app está abierta en el modo de ventanas de escritorio, otras apps también se abren en ventanas de escritorio.
Los usuarios también pueden invocar el modo de ventanas de escritorio desde el menú que aparece debajo del controlador de la ventana cuando presionas o haces clic en el controlador, o bien usas la combinación de teclas Meta key (Windows, Command, or Search) + Ctrl + Down.
Para salir del modo de ventanas de escritorio, los usuarios deben cerrar todas las ventanas activas o tomar el controlador de la ventana en la parte superior de una ventana de escritorio y arrastrar la app a la parte superior de la pantalla. La combinación de teclas Meta + H también sale del modo de ventanas de escritorio y vuelve a ejecutar las apps en pantalla completa.
Para volver al modo de ventanas de escritorio, presiona o haz clic en el mosaico de espacio de escritorio en la pantalla Recientes.
Optimiza el diseño de tu app para un entorno similar al de una computadora
El diseño para una experiencia de escritorio puede diferir significativamente del diseño para dispositivos móviles debido al aumento del espacio de la pantalla, la precisión de la entrada del mouse y el teclado, y la expectativa de una alta productividad.
Jetpack WindowManager proporciona una API con opiniones para ayudar a los desarrolladores a decidir cuándo mostrar una IU de escritorio, que suele tener una mayor densidad de información, diferentes patrones de navegación y optimización de las interacciones del mouse.
lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity) .collect { windowEngagementInfo -> if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){ showDesktopOptimizedUI() }else { showTouchOptimizedUI() } } } }
Para obtener más información, consulta Diseño para computadoras.
Modo de compatibilidad y capacidad de cambio de tamaño
En el modo de ventanas de escritorio, las apps con orientación bloqueada se pueden redimensionar libremente. Eso significa que, incluso si una actividad está bloqueada en orientación vertical, los usuarios pueden cambiar el tamaño de la app a una ventana de orientación horizontal.
Las apps declaradas como no redimensionables (es decir, resizeableActivity = false) tienen
su IU escalada mientras mantienen la misma relación de aspecto.
Las apps de cámara que bloquean la orientación o se declaran como no redimensionables tienen un tratamiento especial para sus visores de cámara: la ventana es completamente redimensionable, pero el visor mantiene la misma relación de aspecto. Si se supone que las apps siempre se ejecutan en orientación vertical u horizontal, las apps codifican o hacen suposiciones que conducen a errores de cálculo de la orientación o la relación de aspecto de la vista previa o la imagen capturada, lo que da como resultado imágenes estiradas, laterales o invertidas.
Hasta que las apps estén listas para implementar visores de cámara completamente responsivos, el tratamiento especial proporciona una experiencia del usuario más básica que mitiga los efectos que pueden causar las suposiciones incorrectas.
Para obtener más información sobre el modo de compatibilidad para apps de cámara, consulta Modo de compatibilidad del dispositivo.
Inserciones de encabezado personalizables
Todas las apps que se ejecutan en el modo de ventanas de escritorio tienen una barra de encabezado, incluso en el modo envolvente. Puedes personalizar esta barra para evitar que se oculte el contenido de tu app y dibujar elementos de IU personalizados directamente en el espacio del encabezado.
Implementación
Para dibujar contenido personalizado en la barra de encabezado, el primer paso es hacer que el fondo de la barra de encabezado sea transparente. Para ello, puedes usar la
APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND marca con el
WindowInsetsController.
window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND, WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND )
Una vez que la barra de encabezado sea transparente, podrás aplicar estilo al área del encabezado para que coincida con el diseño de tu app. Usa WindowInsets.isCaptionBarVisible para detectar si la barra está
presente y aplicar la altura o el relleno adecuados a tu diseño.
@OptIn(ExperimentalLayoutApi::class) @Composable fun CaptionBar() { if (WindowInsets.isCaptionBarVisible) { Row( modifier = Modifier .windowInsetsTopHeight(WindowInsets.captionBar) .fillMaxWidth() .background(if (isSystemInDarkTheme()) Color.White else Color.Black), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Caption Bar Title", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(4.dp) ) } } }
setSystemBarsAppearance(appearance,mask): Configura el estilo visual de las barras del sistema. El primer parámetro define las marcas de apariencia de destino, mientras que el segundo actúa como una máscara para controlar qué marcas específicas se modifican.windowInsetsTopHeight(): Establece automáticamente la altura de tu elemento componible para que coincida con la barra de encabezado del sistema, lo que ayuda a que tu fondo personalizado llene el área de título sin codificar valores de píxeles.WindowInsets.captionBar: Proporciona las dimensiones de los controles del modo de ventanas de escritorio (Cerrar, Maximizar, etcétera), lo que permite que tu IU se escale o se oculte automáticamente cuando ingresas o sales del modo de ventanas de escritorio.
Para obtener más información, consulta Acerca de las inserciones de ventanas. Además de un título, puedes mostrar otros elementos de la IU en la barra de título, como pestañas (como en Google Chrome), barras de búsqueda o avatares de perfil.
Interfaz de usuario
Para evitar superponer tu IU con los botones del sistema, Android 15 proporciona el
WindowInsets#getBoundingRects() método. El método muestra una lista de
Rect objetos que representan las áreas ocupadas por los elementos del sistema. Cualquier espacio restante en la barra de título es una zona segura en la que puedes colocar contenido personalizado de forma segura.
Activa o desactiva la apariencia de los elementos de título del sistema para los temas claros y oscuros con
APPEARANCE_LIGHT_CAPTION_BARS. Accede a las inserciones con
WindowInsets.Companion.captionBar() en Compose o
WindowInsets.Type.captionBar() en Views.
Para obtener más información, consulta Acerca de las inserciones de ventanas.
Compatibilidad con instancias y tareas múltiples
La multitarea es fundamental para el modo de ventanas de escritorio, y permitir varias instancias de tu app puede aumentar en gran medida la productividad de los usuarios.
A partir de Android 15, puedes usar
PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. Si configuras esta propiedad en AndroidManifest.xml, especificas que la IU del sistema debe proporcionar opciones (como un botón "Ventana nueva") para que la app se inicie en varias instancias.
<application>
<property
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
android:value="true" />
</application>
Nota: En el modo de ventanas de escritorio y otros entornos multiventana, las tareas nuevas se abren en una ventana nueva, por lo que debes verificar el recorrido del usuario cada vez que tu app inicie varias tareas.
Administra instancias de apps con gestos de arrastre
En el modo multiventana, los usuarios pueden iniciar una instancia nueva de la app arrastrando un elemento de la IU (como una pestaña o un documento) fuera de la ventana de la app. Los usuarios también pueden mover elementos entre diferentes instancias de la misma app.
Transfiere datos con la función de arrastrar y soltar
Para configurar un elemento componible como fuente de arrastre para la función de arrastrar y soltar de instancias múltiples
lo que permite a los usuarios arrastrar contenido a otra instancia de tu app o crear una
nueva instancia soltando contenido en un área vacía de la pantalla, usa el
dragAndDropSource modificador. En su expresión lambda, muestra
DragAndDropTransferData, pasando el ClipData que contiene los datos que se
transferirán y las marcas para configurar el comportamiento de instancias múltiples.
Android 15 presenta dos marcas clave para el modo de ventanas de estilo de escritorio y las interacciones de instancias múltiples:
DRAG_FLAG_GLOBAL_SAME_APPLICATION: Indica que una operación de arrastre puede cruzar los límites de la ventana (para varias instancias de la misma aplicación). CuandostartDragAndDrop()se llama con esta marca establecida, solo las ventanas visibles que pertenecen a la misma aplicación pueden participar en la operación de arrastre y recibir el contenido arrastrado.
Modifier.dragAndDropSource { _ -> DragAndDropTransferData( clipData = ClipData.newPlainText("label", "Your data"), flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION ) }
DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: Permite a los usuarios iniciar una instancia nueva de tu app soltando el contenido arrastrado en un área vacía de la pantalla, si ninguna otra ventana controla la acción de soltar.- Cuando uses esta marca, debes proporcionar un
IntentSenderconClipData.Item.Builder#setIntentSender(), que el sistema usa para iniciar la actividad nueva si se produce una acción de soltar no controlada.
- Cuando uses esta marca, debes proporcionar un
Modifier.dragAndDropSource { _ -> val intent = Intent.makeMainActivity(activity.componentName).apply { putExtra("EXTRA_ITEM_ID", itemId) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT } val pendingIntent = PendingIntent.getActivity( activity, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val data = ClipData( "Item $itemId", arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT), ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build() ) DragAndDropTransferData( clipData = data, flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG, ) }
Recibe datos transferidos
Para aceptar datos de otra instancia, usa el dragAndDropTarget modificador.
Debes solicitar permisos de forma explícita si los datos provienen de una instancia o app diferente.
Modifier.dragAndDropTarget( shouldStartDragAndDrop = { event -> event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) }, target = object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { requestDragAndDropPermissions(activity, event.toAndroidDragEvent()) val clipData = event.toAndroidDragEvent().clipData val item = clipData?.getItemAt(0)?.text if (item != null) { // Process the dropped text item here } return item != null } } )
Pasos clave:
- Filtro: Usa
shouldStartDragAndDroppara verificar si se admiten los datos entrantes (tipo de MIME). - Permisos: Llama a
requestDragAndDropPermissions(event)para acceder a los datos. - Control: Extrae los datos en la devolución de llamada
onDrop.
Optimizaciones adicionales
Personaliza los inicios de apps y las apps de transición del modo de ventanas de escritorio a pantalla completa.
Especifica el tamaño y la posición predeterminados
No todas las apps, incluso si son redimensionables, necesitan una ventana grande para ofrecer valor al usuario. Puedes
usar el ActivityOptions#setLaunchBounds() método para especificar un tamaño y una posición predeterminados cuando se inicia una actividad.
Ingresa a pantalla completa desde el espacio de escritorio
Las apps pueden pasar a pantalla completa llamando a Activity#requestFullScreenMode(). El método muestra la app en pantalla completa directamente desde el modo de ventanas de escritorio.