Supporta il windowing del desktop

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 accedervi rapidamente.
  • La nuova barra dell'intestazione personalizzabile decora la parte superiore di ogni finestra con controlli come Riduci a icona e Ingrandisci.
Figura 1. Finestre delle app su un tablet.

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.

Figura 2. Tieni premuto e trascina il punto di trascinamento della finestra dell'app per accedere alle finestre delle app.

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.

Ridimensionamento e modalità di 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.

Figura 3. Ridimensionamento della finestra di un'app con orientamento verticale in orizzontale.

L'UI delle app dichiarate come app non ridimensionabili (ovvero resizeableActivity = false) viene scalata mantenendo lo stesso formato.

Figura 4. L'UI di un'app non ridimensionabile viene scalata man mano che la finestra viene ridimensionata.

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.

Figura 5. Il mirino della fotocamera mantiene le proporzioni durante il ridimensionamento della finestra.

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 evitare che i contenuti della tua app vengano oscurati e per disegnare elementi UI personalizzati direttamente nello spazio dell'intestazione.

Chrome prima e dopo l'implementazione delle intestazioni personalizzate.
Figura 6. Chrome prima e dopo l'implementazione delle intestazioni personalizzate.

Implementazione

Per disegnare contenuti personalizzati nella barra dell'intestazione, il primo passaggio consiste nel rendere trasparente lo sfondo della barra dell'intestazione. Puoi farlo utilizzando il flag APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND con 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 personalizzare l'area dell'intestazione in modo che corrisponda al design della tua 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 Composable in modo che corrisponda alla barra dell'intestazione del sistema, aiutando lo sfondo personalizzato a riempire l'area dei sottotitoli codificando i valori dei pixel.

  • WindowInsets.captionBar: fornisce le dimensioni per i controlli delle finestre del computer (Chiudi, Ingrandisci e così via), consentendo alla UI di scalare o nascondersi automaticamente quando si entra o si esce dalla modalità app in finestre.

Per saperne di più, consulta Informazioni sugli insetti delle finestre. Oltre a un titolo, puoi visualizzare altri elementi dell'interfaccia utente nella barra delle didascalie, come schede (come in Google Chrome), barre di ricerca o avatar del profilo.

Interfaccia utente

Per evitare di sovrapporre la tua UI ai pulsanti di sistema, Android 15 fornisce il metodo WindowInsets#getBoundingRects(). Il metodo restituisce un elenco di oggetti Rect che rappresentano le aree occupate dagli elementi di sistema. Lo spazio rimanente nella barra dei sottotitoli codificati è una area di sicurezza in cui puoi inserire in sicurezza contenuti personalizzati.

Attiva/disattiva l'aspetto degli elementi di sistema dei sottotitoli codificati per i temi chiaro e scuro utilizzando APPEARANCE_LIGHT_CAPTION_BARS. Accedi agli inserti utilizzando WindowInsets.Companion.captionBar() in Composizione o WindowInsets.Type.captionBar() in Visualizzazioni.

Per saperne di più, consulta Informazioni sugli insetti 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. Se imposti questa proprietà nel tuo AndroidManifest.xml, specifichi che l'interfaccia utente 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'interfaccia utente (ad esempio una scheda o un documento) fuori dalla finestra dell'app. Gli utenti possono anche spostare gli elementi tra diverse istanze della stessa app.

Figura 7. Avvia una nuova istanza di Chrome trascinando una scheda fuori dalla finestra del desktop.

Trasferire i dati con il trascinamento

Per configurare un elemento componibile come origine di trascinamento per il trascinamento della selezione multi-istanza, che consente 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 modificatore dragAndDropSource. Nella relativa 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 l'utilizzo delle app in finestre in stile desktop e 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). Quando startDragAndDrop() viene chiamato con questo flag impostato, solo le finestre visibili appartenenti alla stessa applicazione possono partecipare all'operazione di trascinamento e ricevere i contenuti trascinati.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

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 modificatore dragAndDropTarget. 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 shouldStartDragAndDrop per verificare se i dati in entrata (tipo MIME) sono supportati.
  • Autorizzazioni: chiama requestDragAndDropPermissions(event) per accedere ai dati.
  • Handle: estrai i dati nel callback onDrop.

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 metodo ActivityOptions#setLaunchBounds() 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.