Supporta il windowing del desktop

Le finestre delle app consentono agli utenti di eseguire più app contemporaneamente in finestre di app ridimensionabili per un'esperienza versatile e simile a quella del computer.

Nella figura 1 puoi vedere l'organizzazione dello schermo con il ridimensionamento delle finestre del computer abilitato. Aspetti importanti:

  • Gli utenti possono eseguire più app affiancate 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.
Il display di un tablet che mostra più app in esecuzione in finestre ridimensionabili con una barra delle app in basso.
Figura 1. Finestre delle app su un tablet.

Per impostazione predefinita, le app si aprono a schermo intero sui tablet Android. Per avviare un'app nella modalità di visualizzazione a finestre del computer, tieni premuta la maniglia della finestra nella parte superiore dello schermo e trascinala all'interno dell'interfaccia utente, come mostrato nella figura 2.

Quando un'app è aperta in una finestra della modalità desktop, anche le altre app si aprono in finestre della modalità desktop.

Figura 2. Premi, tieni premuta e trascina la maniglia della finestra dell'app per accedere alla modalità finestra del desktop.

Gli utenti possono anche richiamare la gestione delle finestre del desktop dal menu visualizzato sotto la maniglia della finestra quando toccano o fanno clic sulla maniglia o utilizzano la scorciatoia da tastiera Tasto Meta (Windows, Comando o Ricerca) + Ctrl + Freccia giù.

Gli utenti escono dalla modalità finestre chiudendo tutte le finestre attive o trascinando la maniglia della finestra nella parte superiore di una finestra del computer e trascinando l'app nella parte superiore dello schermo. La scorciatoia da tastiera Meta + H esce anche dalla modalità di visualizzazione a finestre del computer e riavvia le app a schermo intero.

Per tornare al ridimensionamento delle finestre del desktop, tocca o fai clic sul riquadro dello spazio di lavoro nella schermata Recenti.

Ridimensionamento e modalità di compatibilità

Nel sistema di finestre delle app, le app con orientamento bloccato sono liberamente ridimensionabili. 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.

Le app dichiarate non ridimensionabili (ovvero, resizeableActivity = false) hanno la UI scalata mantenendo lo stesso formato.

Figura 4. L'interfaccia utente 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 non ridimensionabili hanno un trattamento speciale per i mirini della fotocamera: la finestra è completamente ridimensionabile, ma il mirino mantiene le stesse proporzioni. Supponendo che le app vengano sempre eseguite in verticale o orizzontale, le app codificano 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 della videocamera completamente reattivi, il trattamento speciale offre un'esperienza utente più semplice che mitiga gli effetti che potrebbero essere causati da ipotesi errate.

Per scoprire di più sulla modalità di compatibilità per le app della videocamera, consulta Modalità di compatibilità del dispositivo.

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

Spazi interni personalizzabili dell'intestazione

Tutte le app in esecuzione in finestre del desktop hanno una barra dell'intestazione, anche in modalità immersiva. Verifica che i contenuti dell'app non siano oscurati dalla barra dell'intestazione. La barra dell'intestazione è un tipo di barra delle didascalie incorporata: WindowInsets.Companion.captionBar(); nelle visualizzazioni, WindowInsets.Type.captionBar(), che fa parte delle barre di sistema.

Puoi scoprire di più sulla gestione degli insetti in Visualizzare i contenuti edge-to-edge nell'app e gestire gli insetti della finestra in Compose.

Anche la barra dell'intestazione è personalizzabile. Android 15 ha introdotto il tipo di aspetto APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND per rendere la barra dell'intestazione trasparente e consentire alle app di disegnare contenuti personalizzati al suo interno.

Le app diventano quindi responsabili dello stile della parte superiore dei contenuti in modo che assomigli alla barra dei sottotitoli codificati (sfondo, contenuti personalizzati e così via), ad eccezione degli elementi dei sottotitoli codificati di sistema (pulsanti Chiudi e Ingrandisci), che vengono disegnati dal sistema sulla barra dei sottotitoli codificati trasparente sopra l'app.

Le app possono attivare/disattivare l'aspetto degli elementi di sistema all'interno della didascalia per i temi chiaro e scuro utilizzando APPEARANCE_LIGHT_CAPTION_BARS, in modo simile a come vengono attivate/disattivate la barra di stato e la barra di navigazione.

Android 15 ha introdotto anche il metodo WindowInsets#getBoundingRects(), che consente alle app di esaminare in modo più dettagliato gli inset della barra delle didascalie. Le app possono distinguere tra le aree in cui il sistema disegna gli elementi di sistema e le aree inutilizzate in cui le app possono inserire contenuti personalizzati senza sovrapporsi agli elementi di sistema.

L'elenco degli oggetti Rect restituiti dall'API indica le regioni di sistema da evitare. Lo spazio rimanente (calcolato sottraendo i rettangoli dalle barre delle didascalie) è quello in cui l'app può disegnare senza sovrapporsi agli elementi di sistema e con la possibilità di ricevere input.

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

Per impostare i rettangoli di esclusione dei gesti di sistema per un'intestazione personalizzata, implementa quanto segue nella visualizzazione o nel composable:

// In a custom View's onLayout or a similar lifecycle method
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed) {
        // Calculate the height of your custom header
        val customHeaderHeight = 100 // Replace with your actual header height in pixels

        // Create a Rect covering your custom header area
        val exclusionRect = Rect(0, 0, width, customHeaderHeight)

        // Set the exclusion rects for the system
        systemGestureExclusionRects = listOf(exclusionRect)
    }
}

Supporto per il multitasking e più istanze

Il multitasking è al centro delle finestre delle app e consentire più istanze della tua app può aumentare notevolmente la produttività degli utenti.

Android 15 introduce PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, che le app possono impostare per specificare che la UI di sistema deve essere mostrata per consentire l'avvio dell'app come più istanze.

Puoi dichiarare PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI nel AndroidManifest.xml della tua app all'interno del tag <activity>:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:resizeableActivity="true">
    <meta-data
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</activity>

Gestire 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 di visualizzazione fuori dalla finestra dell'app. Gli utenti possono anche spostare gli elementi tra le istanze della stessa app.

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

Android 15 introduce due flag per personalizzare il comportamento di trascinamento:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: Indica che un trascinamento non gestito deve essere delegato al sistema per essere avviato se nessuna finestra visibile gestisce il rilascio. Quando utilizzi questo flag, il chiamante deve fornire ClipData con un ClipData.Item che contiene un IntentSender immutabile a un'attività da avviare (vedi ClipData.Item.Builder#setIntentSender()). Il sistema può avviare l'intent o meno in base a fattori come le dimensioni dello schermo o la modalità finestra correnti. Se il sistema non avvia l'intent, l'intent viene annullato tramite il normale flusso di trascinamento.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: Indica che un'operazione di trascinamento può superare i limiti della finestra (per più istanze della stessa applicazione).

    Quando viene chiamato [startDragAndDrop()][20] con questo flag impostato, solo le finestre visibili appartenenti alla stessa applicazione possono partecipare all'operazione di trascinamento e ricevere i contenuti trascinati.

L'esempio seguente mostra come utilizzare questi flag con startDragAndDrop():

// Assuming 'view' is the View that initiates the drag
view.setOnLongClickListener {
    // Create an IntentSender for the activity you want to launch
    val launchIntent = Intent(view.context, NewInstanceActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        view.context,
        0,
        launchIntent,
        PendingIntent.FLAG_IMMUTABLE // Ensure the PendingIntent is immutable
    )

    // Build the ClipData.Item with the IntentSender
    val item = ClipData.Item.Builder()
        .setIntentSender(pendingIntent.intentSender)
        .build()

    // Create ClipData with a simple description and the item
    val dragData = ClipData(
        ClipDescription("New Instance Drag", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
        item
    )

    // Combine the drag flags
    val dragFlags = View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG or
                    View.DRAG_FLAG_GLOBAL_SAME_APPLICATION

    // Start the drag operation
    view.startDragAndDrop(
        dragData,                     // The ClipData to drag
        View.DragShadowBuilder(view), // A visual representation of the dragged item
        null,                         // Local state object (not used here)
        dragFlags                     // The drag flags
    )
    true // Indicate that the long click was consumed
}
Figura 8. Spostare una scheda tra due istanze dell'app Chrome.

Ottimizzazioni aggiuntive

Personalizza l'avvio delle app e la transizione dalle app dalla modalità di visualizzazione a finestre del computer a schermo intero.

Specificare dimensioni e posizione predefinite

Non tutte le app, anche se ridimensionabili, hanno bisogno di una finestra grande per offrire valore all'utente. Puoi utilizzare il metodo ActivityOptions#setLaunchBounds() per specificare una dimensione e una posizione predefinite all'avvio di un'attività.

Ecco un esempio di come impostare i limiti di avvio per un'attività:

val options = ActivityOptions.makeBasic()

// Define the desired launch bounds (left, top, right, bottom in pixels)
val launchBounds = Rect(100, 100, 700, 600) // Example: 600x500 window at (100,100)

// Apply the launch bounds to the ActivityOptions
options.setLaunchBounds(launchBounds)

// Start the activity with the specified options
val intent = Intent(this, MyActivity::class.java)
startActivity(intent, options.toBundle())

Attivare 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 dal ridimensionamento delle finestre del desktop.

Per richiedere la modalità a schermo intero da un'attività, utilizza il seguente codice:

// In an Activity
fun enterFullScreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 15 (U)
        requestFullScreenMode()
    }
}