Unterstützung von Desktop-Fenstern

Mit dem Desktop-Freiform-Fenster können Nutzer mehrere Apps gleichzeitig in App-Fenstern mit anpassbarer Größe ausführen. So erhalten sie ein vielseitiges, desktopähnliches Erlebnis.

Abbildung 1 zeigt die Organisation des Bildschirms, wenn die Desktop-Fensterfunktion aktiviert ist. Hinweise:

  • Nutzer können mehrere Apps gleichzeitig nebeneinander ausführen.
  • Die Taskleiste befindet sich in einer festen Position unten auf dem Display und zeigt die aktiven Apps an. Nutzer können Apps für den Schnellzugriff anpinnen.
  • Die neue anpassbare Kopfzeile oben in jedem Fenster enthält Steuerelemente wie „Minimieren“ und „Maximieren“.
Ein Tablet-Display, auf dem mehrere Apps in Fenstern mit anpassbarer Größe ausgeführt werden. Unten ist eine Taskleiste zu sehen.
Abbildung 1. Desktop-Freiform-Fenster auf einem Tablet

Apps werden auf Android-Tablets standardmäßig im Vollbildmodus geöffnet. Wenn Sie eine App im Desktop-Fenstermodus starten möchten, halten Sie den Fensterziehpunkt oben auf dem Bildschirm gedrückt und ziehen Sie ihn in der Benutzeroberfläche, wie in Abbildung 2 dargestellt.

Wenn eine App in einem Desktop-Freiform-Fenster geöffnet ist, werden auch andere Apps in Desktop-Fenstern geöffnet.

Abbildung 2. Halten Sie den App-Fenstergriff gedrückt und ziehen Sie ihn, um den Desktop-Fenstermodus zu aktivieren.

Nutzer können die Desktop-Fensterverwaltung auch über das Menü aufrufen, das unter dem Fenster-Handle angezeigt wird, wenn sie auf das Handle tippen oder klicken oder die Tastenkombination Meta-Taste (Windows, Befehl oder Suche) + Strg + Nach unten verwenden.

Nutzer beenden die Desktop-Fensterdarstellung, indem sie alle aktiven Fenster schließen oder den Fensterziehpunkt oben in einem Desktop-Fenster ziehen und die App an den oberen Bildschirmrand ziehen. Mit der Tastenkombination Meta + H wird der Desktop-Freiform-Fenstermodus ebenfalls beendet und Apps werden wieder im Vollbildmodus ausgeführt.

Wenn Sie zur Desktop-Fensterdarstellung zurückkehren möchten, tippen oder klicken Sie auf dem Bildschirm „Letzte“ auf die Kachel für den Desktop-Arbeitsbereich.

Größenanpassung und Kompatibilitätsmodus

Bei der Desktop-Fensterdarstellung können Apps mit gesperrter Ausrichtung frei in der Größe angepasst werden. Das bedeutet, dass Nutzer die App auch dann in ein Fenster im Querformat umwandeln können, wenn eine Aktivität auf das Hochformat beschränkt ist.

Abbildung 3. Die Größe des Fensters einer App, die auf das Hochformat beschränkt ist, wird auf das Querformat geändert.

Bei Apps, die als nicht anpassbar deklariert wurden (d. h. resizeableActivity = false), wird die Benutzeroberfläche skaliert, wobei das Seitenverhältnis beibehalten wird.

Abbildung 4. Die Benutzeroberfläche einer App, deren Größe nicht geändert werden kann, wird skaliert, wenn sich die Größe des Fensters ändert.

Kamera-Apps, die die Ausrichtung sperren oder als nicht anpassbar deklariert werden, werden für ihre Kamerasucher speziell behandelt: Das Fenster ist vollständig anpassbar, aber der Sucher behält das gleiche Seitenverhältnis bei. Wenn Apps immer im Hoch- oder Querformat ausgeführt werden, werden in den Apps Annahmen hartcodiert oder anderweitig getroffen, die zu Fehlberechnungen der Ausrichtung oder des Seitenverhältnisses der Vorschau oder des aufgenommenen Bildes führen. Das Ergebnis sind gestreckte, seitlich oder auf dem Kopf stehende Bilder.

Bis Apps bereit sind, vollständig reaktionsfähige Kamerasuchfelder zu implementieren, bietet die Sonderbehandlung eine einfachere Nutzererfahrung, die die Auswirkungen falscher Annahmen abmildert.

Weitere Informationen zum Kompatibilitätsmodus für Kamera-Apps finden Sie unter Gerätekompatibilitätsmodus.

Abbildung 5. Das Seitenverhältnis des Kamerasuchers bleibt beim Ändern der Fenstergröße erhalten.

Anpassbare Header-Insets

Alle Apps, die in Desktop-Fenstern ausgeführt werden, haben eine Kopfzeile, auch im Immersive Mode. Prüfen Sie, ob die Inhalte Ihrer App durch die Kopfzeile verdeckt werden. Die Kopfzeile ist eine eingebettete Untertitelzeile: WindowInsets.Companion.captionBar(); in Ansichten WindowInsets.Type.captionBar()>, die Teil der Systemleisten ist.

Weitere Informationen zum Umgang mit Insets finden Sie unter Inhalte in Ihrer App von Kante zu Kante anzeigen und Fenster-Insets in Compose verarbeiten.

Auch die Kopfzeile lässt sich anpassen. In Android 15 wurde der Darstellungstyp APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND eingeführt, um die Kopfzeile transparent zu machen, damit Apps benutzerdefinierte Inhalte in der Kopfzeile darstellen können.

Apps sind dann dafür verantwortlich, den oberen Teil ihrer Inhalte so zu gestalten, dass er wie die Titelleiste aussieht (Hintergrund, benutzerdefinierte Inhalte usw.). Die System-Titelleistenelemente (Schließen- und Maximieren-Schaltflächen) werden jedoch vom System in der transparenten Titelleiste über der App gezeichnet.

Apps können das Erscheinungsbild der Systemelemente in der Beschriftung für helle und dunkle Designs mit APPEARANCE_LIGHT_CAPTION_BARS umschalten, ähnlich wie die Statusleiste und die Navigationsleiste.

In Android 15 wurde auch die Methode WindowInsets#getBoundingRects() eingeführt, mit der Apps die Insets der Untertitel-Leiste genauer untersuchen können. Apps können zwischen Bereichen unterscheiden, in denen das System Systemelemente zeichnet, und nicht genutzten Bereichen, in denen Apps benutzerdefinierte Inhalte platzieren können, ohne Systemelemente zu überlappen.

Die von der API zurückgegebene Liste der Rect-Objekte gibt Systemregionen an, die vermieden werden sollten. Im verbleibenden Bereich (berechnet durch Subtrahieren der Rechtecke von den Insets der Untertitelzeile) kann die App Inhalte rendern, ohne Systemelemente zu überlappen, und Eingaben empfangen.

Chrome vor und nach der Implementierung benutzerdefinierter Header.
Abbildung 6: Chrome vor und nach der Implementierung benutzerdefinierter Header.
eine priorisierte Eingabebehandlung für ihre App-Benutzeroberfläche im anpassbaren Bereich anfordern.

Wenn Sie Ausschlussrechtecke für Systemgesten für einen benutzerdefinierten Header festlegen möchten, implementieren Sie Folgendes in Ihrer Ansicht oder 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)
    }
}

Multitasking und Unterstützung mehrerer Instanzen

Multitasking ist das Herzstück des Desktop-Freiform-Fensters. Wenn Sie mehrere Instanzen Ihrer App zulassen, kann die Produktivität der Nutzer erheblich gesteigert werden.

In Android 15 wird PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI eingeführt. Apps können diesen Wert festlegen, um anzugeben, dass die System-UI für die App angezeigt werden soll, damit sie als mehrere Instanzen gestartet werden kann.

Sie können PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI in der AndroidManifest.xml Ihrer App innerhalb des <activity>-Tags deklarieren:

<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>

App-Instanzen mit Drag-and-drop-Gesten verwalten

Im Mehrfenstermodus können Nutzer eine neue App-Instanz starten, indem sie ein Ansichtselement aus dem App-Fenster ziehen. Nutzer können auch Elemente zwischen Instanzen derselben App verschieben.

Abbildung 7. Starten Sie eine neue Chrome-Instanz, indem Sie einen Tab aus dem Desktopfenster ziehen.

In Android 15 werden zwei Flags eingeführt, mit denen das Ziehverhalten angepasst werden kann:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: Gibt an, dass ein nicht verarbeiteter Drag an das System delegiert werden soll, um gestartet zu werden, wenn kein sichtbares Fenster den Drop verarbeitet. Wenn dieses Flag verwendet wird, muss der Aufrufer ClipData mit einem ClipData.Item bereitstellen, das eine unveränderliche IntentSender für eine zu startende Aktivität enthält (siehe ClipData.Item.Builder#setIntentSender()). Das System kann den Intent basierend auf Faktoren wie der aktuellen Bildschirmgröße oder dem Fenstermodus starten oder nicht. Wenn der Intent nicht vom System gestartet wird, wird er über den normalen Drag-Vorgang abgebrochen.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: Gibt an, dass ein Drag-Vorgang Fenstergrenzen überschreiten kann (für mehrere Instanzen derselben Anwendung).

    Wenn [startDragAndDrop()][20] mit diesem Flag aufgerufen wird, können nur sichtbare Fenster derselben Anwendung am Drag-Vorgang teilnehmen und die gezogenen Inhalte empfangen.

Das folgende Beispiel zeigt, wie Sie diese Flags mit startDragAndDrop() verwenden:

// 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
}
Abbildung 8. Tab zwischen zwei Instanzen der Chrome App verschieben.

Zusätzliche Optimierungen

Sie können das Starten von Apps anpassen und Apps vom Desktop-Freiform-Fenstermodus in den Vollbildmodus wechseln.

Standardgröße und ‑position festlegen

Nicht alle Apps, auch wenn sie in der Größe angepasst werden können, benötigen ein großes Fenster, um Nutzern einen Mehrwert zu bieten. Mit der Methode ActivityOptions#setLaunchBounds() können Sie eine Standardgröße und ‑position für den Start einer Aktivität festlegen.

Hier ist ein Beispiel dafür, wie Sie Startgrenzen für eine Aktivität festlegen:

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())

Vollbildmodus über den Desktopbereich aktivieren

Apps können in den Vollbildmodus wechseln, indem sie Activity#requestFullScreenMode() aufrufen. Mit dieser Methode wird die App direkt aus dem Desktop-Fenstersystem im Vollbildmodus angezeigt.

Verwenden Sie den folgenden Code, um den Vollbildmodus über eine Aktivität anzufordern:

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