Le finestre delle app consentono agli utenti di eseguire più app contemporaneamente in finestre ridimensionabili per un'esperienza versatile, proprio come su un desktop.
Nella figura 1 puoi vedere l'organizzazione dello schermo con le app in finestra abilitate. Aspetti da considerare:
- Gli utenti possono eseguire più app una accanto all'altra per utilizzarle contemporaneamente.
- La barra delle app è in una posizione fissa nella parte inferiore del display e mostra le app in esecuzione. Gli utenti possono bloccare le app per un accesso rapido.
- La nuova barra dell'intestazione personalizzabile decora la parte superiore di ogni finestra con controlli come Riduci a icona e Ingrandisci.
Per impostazione predefinita, le app si aprono a schermo intero sui tablet Android. Per lanciare un'app in finestra, tieni premuto il punto di trascinamento della finestra nella parte superiore dello schermo e trascinalo all'interno dell'UI, come mostrato nella figura 2.
Quando un'app è aperta nelle finestre, anche le altre app si aprono in finestra.
Gli utenti possono anche richiamare le app in finestra dal menu visualizzato sotto il punto di trascinamento della finestra toccando o facendo clic sul punto di trascinamento, o utilizzando la scorciatoia da tastiera Tasto Meta (Windows, Comando o Ricerca) + Ctrl + Freccia giù.
Gli utenti possono uscire dalle finestre delle app chiudendo tutte le finestre attive o selezionando il punto di trascinamento nella parte superiore di un'app in finestra e trascinando l'app nella parte superiore dello schermo. Anche la scorciatoia da tastiera Meta + H consente di uscire dalle finestre delle app e di eseguire nuovamente le app in modalità a schermo intero.
Per tornare alle finestre delle app, tocca o seleziona il riquadro dello spazio del desktop nella schermata Recenti.
Ottimizzare il layout dell'app per un ambiente simile a quello di un computer
La progettazione per un'esperienza desktop può differire in modo significativo dalla progettazione per dispositivi mobili a causa del maggiore spazio sullo schermo, della precisione dell'input da tastiera e mouse e dell'aspettativa di un'elevata produttività.
Jetpack WindowManager fornisce un'API che aiuta gli sviluppatori a decidere quando mostrare un'UI desktop, che in genere ha una maggiore densità di informazioni, pattern di navigazione diversi e interazioni con il mouse ottimizzate.
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() } } } }
Per saperne di più, consulta Progettare per il desktop.
Modalità di ridimensionamento e compatibilità
Nelle finestre delle app, le app con orientamento bloccato possono essere ridimensionate liberamente. Ciò significa che, anche se un'attività è bloccata sull'orientamento verticale, gli utenti possono comunque ridimensionare l'app in una finestra con orientamento orizzontale.
L'UI delle app dichiarate come app non ridimensionabili (ovvero resizeableActivity = false) viene scalata
mantenendo lo stesso formato.
Le app della fotocamera che bloccano l'orientamento o sono dichiarate come non ridimensionabili hanno un trattamento speciale per i mirini della fotocamera: la finestra è completamente ridimensionabile, ma il mirino mantiene le stesse proporzioni. Se le app vengono sempre eseguite in verticale o orizzontale, le app vengono impostate come hardcoded o fanno altrimenti ipotesi che portano a errori di calcolo dell'orientamento o delle proporzioni dell'anteprima o dell'immagine acquisita, con conseguenti immagini allungate, laterali o capovolte.
Finché le app non saranno pronte a implementare mirini completamente reattivi, il trattamento speciale offre un'esperienza utente più semplice che mitiga gli effetti causati da ipotesi errate.
Per scoprire di più sulla modalità di compatibilità per le app fotocamera, consulta Modalità di compatibilità del dispositivo.
Inset dell'intestazione personalizzabili
Tutte le app in esecuzione nelle finestre hanno una barra dell'intestazione, anche in modalità immersiva. Puoi personalizzare questa barra per impedire che i contenuti dell'app vengano oscurati e per disegnare elementi dell'UI personalizzati direttamente nello spazio dell'intestazione.
Implementazione
Per disegnare contenuti personalizzati nella barra dell'intestazione, il primo passo è rendere trasparente lo sfondo della barra dell'intestazione. Puoi farlo utilizzando il
APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND flag con il
WindowInsetsController.
window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND, WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND )
Una volta che la barra dell'intestazione è trasparente, puoi applicare uno stile all'area dell'intestazione in modo che corrisponda al design dell'app. Utilizza WindowInsets.isCaptionBarVisible per rilevare se la barra è
presente e applicare l'altezza o il padding appropriati al layout.
@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 lo stile visivo delle barre di sistema. Il primo parametro definisce i flag di aspetto di destinazione, mentre il secondo funge da maschera per controllare quali flag specifici vengono modificati.windowInsetsTopHeight(): imposta automaticamente l'altezza del tuo elemento componibile in modo che corrisponda alla barra dell'intestazione del sistema, aiutando lo sfondo personalizzato a riempire l'area del titolo senza codificare i valori dei pixel.WindowInsets.captionBar: fornisce le dimensioni per i controlli delle finestre delle app desktop (Chiudi, Ingrandisci e così via), consentendo all'UI di scalare o nascondersi automaticamente quando si entra o si esce dalle finestre delle app desktop.
Per saperne di più, consulta Informazioni sugli inset delle finestre. Oltre a un titolo, puoi visualizzare altri elementi dell'UI nella barra del titolo, come schede (come in Google Chrome), barre di ricerca o avatar del profilo.
Interfaccia utente
Per evitare di sovrapporre l'UI ai pulsanti di sistema, Android 15 fornisce il
WindowInsets#getBoundingRects() metodo. Il metodo restituisce un elenco di
Rect oggetti che rappresentano le aree occupate dagli elementi di sistema. Lo spazio rimanente nella barra del titolo è una area di sicurezza in cui puoi inserire in sicurezza contenuti personalizzati.
Attiva/disattiva l'aspetto degli elementi del titolo di sistema per i temi chiaro e scuro utilizzando
APPEARANCE_LIGHT_CAPTION_BARS. Accedi agli inset utilizzando
WindowInsets.Companion.captionBar() in Compose o
WindowInsets.Type.captionBar() in Views.
Per saperne di più, consulta Informazioni sugli inset delle finestre.
Supporto per multitasking e multi-istanza
Il multitasking è lo scopo principale delle finestre delle app; consentire più istanze della tua app può aumentare notevolmente la produttività degli utenti.
A partire da Android 15, puoi utilizzare
PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. Impostando questa proprietà in AndroidManifest.xml, specifichi che l'UI di sistema deve fornire opzioni (come un pulsante "Nuova finestra") per l'avvio dell'app in più istanze.
<application>
<property
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
android:value="true" />
</application>
Nota: in ambienti con finestre delle app e altri ambienti multi-finestra, le nuove attività si aprono in una nuova finestra, quindi controlla il percorso dell'utente ogni volta che la tua app avvia più attività.
Gestisci le istanze delle app con i gesti di trascinamento
In modalità multi-finestra, gli utenti possono avviare una nuova istanza dell'app trascinando un elemento dell'UI (come una scheda o un documento) fuori dalla finestra dell'app. Gli utenti possono anche spostare gli elementi tra le diverse istanze della stessa app.
Trasferire i dati con il trascinamento
Per configurare un elemento componibile come origine di trascinamento per il trascinamento multi-istanza,
consentendo agli utenti di trascinare i contenuti in un'altra istanza dell'app o di creare una
nuova istanza rilasciando i contenuti in un'area vuota dello schermo, utilizza il
dragAndDropSource modificatore. Nella relativa espressione lambda, restituisci
DragAndDropTransferData, passando ClipData che contiene i dati da
trasferire e i flag per configurare il comportamento multi-istanza.
Android 15 introduce due flag chiave per le finestre in stile desktop e le interazioni multi-istanza:
DRAG_FLAG_GLOBAL_SAME_APPLICATION: indica che un'operazione di trascinamento può superare i limiti della finestra (per più istanze della stessa applicazione). QuandostartDragAndDrop()viene chiamato con questo flag impostato, solo le finestre visibili appartenenti alla stessa applicazione possono partecipare a l'operazione di trascinamento e ricevere i contenuti trascinati.
Modifier.dragAndDropSource { _ -> DragAndDropTransferData( clipData = ClipData.newPlainText("label", "Your data"), flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION ) }
DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: consente agli utenti di avviare una nuova istanza dell'app rilasciando i contenuti trascinati in un'area vuota dello schermo, se nessun'altra finestra gestisce il rilascio.- Quando utilizzi questo flag, devi fornire un
IntentSenderutilizzandoClipData.Item.Builder#setIntentSender(), che il sistema utilizza per avviare la nuova attività se si verifica un rilascio non gestito.
- Quando utilizzi questo flag, devi fornire 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, ) }
Ricevere i dati trasferiti
Per accettare i dati da un'altra istanza, utilizza il dragAndDropTarget modificatore.
Devi richiedere esplicitamente le autorizzazioni se i dati provengono da un'altra istanza o app.
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 } } )
Passaggi chiave:
- Filtro: utilizza
shouldStartDragAndDropper verificare se i dati in entrata (tipo MIME) sono supportati. - Autorizzazioni: chiama
requestDragAndDropPermissions(event)per accedere ai dati. - Gestione: estrai i dati nel
onDropcallback.
Altre ottimizzazioni
Personalizza l'avvio delle app e la transizione dalle app dalla visualizzazione a finestre alla modalità a schermo intero.
Specifica dimensioni e posizione predefinite
Non tutte le app hanno bisogno di una finestra grande per offrire valore all'utente, anche se sono ridimensionabili. Puoi
utilizzare il ActivityOptions#setLaunchBounds() metodo per specificare una dimensione e una posizione predefinite
all'avvio di un'attività.
Attiva la modalità a schermo intero dallo spazio del desktop
Le app possono passare alla modalità a schermo intero chiamando Activity#requestFullScreenMode(). Il metodo mostra l'app a schermo intero direttamente dalle app in finestra.