Verschiedene Bildschirmgrößen unterstützen

Durch die Unterstützung verschiedener Displaygrößen kann Ihre App auf einer Vielzahl von Geräten und von einer großen Anzahl von Nutzern aufgerufen werden.

Damit möglichst viele Displaygrößen unterstützt werden – sowohl verschiedene Gerätebildschirme als auch verschiedene App-Fenster im Multi-Window-Modus – sollten Sie Ihre App-Layouts responsiv und adaptiv gestalten. Responsive/adaptive Layouts bieten unabhängig von der Displaygröße eine optimierte Nutzererfahrung. So kann Ihre App auf Smartphones, Tablets, faltbaren Geräten, ChromeOS-Geräten, im Hoch- und Querformat sowie in konfigurierbaren Displaykonfigurationen wie dem Split-Screen-Modus und dem Desktop-Freiform-Fenster verwendet werden.

Responsive/adaptive Layouts werden an den verfügbaren Anzeigebereich angepasst. Die Änderungen reichen von kleinen Layoutanpassungen, die den verfügbaren Platz ausnutzen (responsives Design), bis hin zum vollständigen Ersetzen eines Layouts durch ein anderes, damit Ihre App für verschiedene Displaygrößen optimiert ist (adaptives Design).

Als deklaratives UI-Toolkit ist Jetpack Compose ideal für das Entwerfen und Implementieren von Layouts, die sich dynamisch ändern, um Inhalte auf verschiedenen Displaygrößen unterschiedlich darzustellen.

Große Layoutänderungen für Composables auf Inhaltsebene explizit machen

Composables auf App- und Inhaltsebene belegen den gesamten für Ihre App verfügbaren Displaybereich. Bei diesen Arten von Composables kann es sinnvoll sein, das Gesamtlayout Ihrer App auf großen Displays zu ändern.

Verwenden Sie keine Werte für physische Hardware, um Layoutentscheidungen zu treffen. Es kann verlockend sein, Entscheidungen auf Grundlage eines festen, greifbaren Werts zu treffen (Ist das Gerät ein Tablet? Hat der physische Bildschirm ein bestimmtes Seitenverhältnis?), aber die Antworten auf diese Fragen sind möglicherweise nicht hilfreich, um den für Ihre Benutzeroberfläche verfügbaren Platz zu ermitteln.

Abbildung 1. Formfaktoren für Smartphones, Faltbare, Tablets und Laptops

Auf Tablets wird eine App möglicherweise im Multi-Window-Modus ausgeführt. Das bedeutet, dass die App den Bildschirm mit einer anderen App teilt. Im Desktop-Fenstermodus oder unter ChromeOS wird eine App möglicherweise in einem Fenster mit anpassbarer Größe ausgeführt. Es kann auch mehr als ein physisches Display geben, z. B. bei einem faltbaren Gerät. In all diesen Fällen ist die physische Bildschirmgröße nicht relevant für die Entscheidung, wie Inhalte angezeigt werden.

Treffen Sie stattdessen Entscheidungen auf Grundlage des tatsächlichen Teils des Bildschirms, der Ihrer App zugewiesen ist. Dieser wird durch die aktuellen Fenstermesswerte beschrieben, die von der Jetpack-Bibliothek WindowManager bereitgestellt werden. Ein Beispiel für die Verwendung von WindowManager in einer Compose-App finden Sie im JetNews-Beispiel.

Wenn Sie Ihre Layouts an den verfügbaren Anzeigebereich anpassen, wird auch der Aufwand für die Unterstützung von Plattformen wie ChromeOS und Formfaktoren wie Tablets und Foldables reduziert.

Nachdem Sie die Messwerte des für Ihre App verfügbaren Bereichs ermittelt haben, wandeln Sie die Rohgröße in eine Fenstergrößenklasse um, wie unter Fenstergrößenklassen verwenden beschrieben. Fenstergrößenklassen sind Breakpoints, die darauf ausgelegt sind, die Einfachheit der App-Logik mit der Flexibilität in Einklang zu bringen, Ihre App für die meisten Displaygrößen zu optimieren.

Fenstergrößenklassen beziehen sich auf das gesamte Fenster Ihrer App. Verwenden Sie die Klassen daher für Layoutentscheidungen, die sich auf das gesamte App-Layout auswirken. Sie können Fenstergrößenklassen als Status übergeben oder zusätzliche Logik ausführen, um abgeleiteten Status zu erstellen, der an verschachtelte Composables übergeben werden kann.

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
    // Decide whether to show the top app bar based on window size class.
    val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND)

    // MyScreen logic is based on the showTopAppBar boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

Bei einem mehrschichtigen Ansatz ist die Logik für die Displaygröße auf einen einzigen Ort beschränkt, anstatt in Ihrer App an vielen Stellen verteilt zu sein, die synchronisiert werden müssen. Ein einzelner Ort erzeugt einen Status, der wie jeder andere App-Status explizit an andere Composables übergeben werden kann. Durch die explizite Übergabe des Status werden einzelne Composables vereinfacht, da sie die Fenstergrößenklasse oder die angegebene Konfiguration zusammen mit anderen Daten übernehmen.

Flexible verschachtelte Composables sind wiederverwendbar

Composables sind wiederverwendbarer, wenn sie an vielen verschiedenen Stellen platziert werden können. Wenn ein Composable an einem bestimmten Ort mit einer bestimmten Größe platziert werden muss, ist es unwahrscheinlich, dass es in anderen Kontexten wiederverwendet werden kann. Das bedeutet auch, dass einzelne, wiederverwendbare Composables nicht implizit von globalen Informationen zur Displaygröße abhängen sollten.

Stellen Sie sich eine verschachtelte Composable vor, die ein Listen-/Detail-Layout implementiert, in dem entweder ein einzelner Bereich oder zwei Bereiche nebeneinander angezeigt werden:

Eine App mit zwei nebeneinander angeordneten Bereichen.
Abbildung 2. App mit einem typischen Listen-/Detail-Layout: 1 ist der Listenbereich, 2 der Detailbereich.

Die Entscheidung für die Liste oder die Detailansicht sollte Teil des Gesamlayouts der App sein. Sie wird also von einer zusammensetzbaren Funktion auf Inhaltsebene weitergegeben:

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

Was ist, wenn Sie möchten, dass ein Composable sein Layout unabhängig vom verfügbaren Anzeigebereich ändert, z. B. eine Karte, auf der zusätzliche Details angezeigt werden, wenn genügend Platz vorhanden ist? Sie möchten eine bestimmte Logik basierend auf einer verfügbaren Displaygröße ausführen. Welche Größe soll das sein?

Abbildung 3. Schmale Karte mit Symbol und Titel sowie breitere Karte mit Symbol, Titel und Kurzbeschreibung.

Verwenden Sie nicht die Größe des tatsächlichen Displays des Geräts. Diese Methode ist für verschiedene Arten von Bildschirmen nicht genau und auch nicht, wenn die App nicht im Vollbildmodus ausgeführt wird.

Da das Composable kein Composable auf Inhaltsebene ist, sollten Sie die Messwerte des aktuellen Fensters nicht direkt verwenden.

Wenn die Komponente mit Padding platziert wird (z. B. mit Insets) oder wenn die App Komponenten wie Navigationsleisten oder App-Leisten enthält, kann sich der für die Composable verfügbare Anzeigebereich erheblich vom gesamten für die App verfügbaren Bereich unterscheiden.

Verwenden Sie die Breite, die der Composable tatsächlich zum Rendern zugewiesen wird. Sie haben zwei Möglichkeiten, diese Breite zu erreichen:

  • Wenn Sie ändern möchten, wo oder wie Inhalte angezeigt werden, verwenden Sie eine Sammlung von Modifizierern oder ein benutzerdefiniertes Layout, um das Layout responsiv zu gestalten. Das kann so einfach sein, dass ein untergeordnetes Element den gesamten verfügbaren Platz ausfüllt, oder dass untergeordnete Elemente in mehreren Spalten angeordnet werden, wenn genügend Platz vorhanden ist.

  • Wenn Sie ändern möchten, was angezeigt wird, verwenden Sie BoxWithConstraints als leistungsstärkere Alternative. BoxWithConstraints bietet Messbeschränkungen, mit denen Sie basierend auf dem verfügbaren Anzeigebereich verschiedene Composables aufrufen können. Dies hat jedoch seinen Preis, da BoxWithConstraints die Komposition bis zur Layoutphase aufschiebt, wenn diese Einschränkungen bekannt sind. Dadurch wird mehr Arbeit während des Layouts ausgeführt.

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

Alle Daten für verschiedene Displaygrößen verfügbar machen

Wenn Sie ein Composable implementieren, das zusätzlichen Displayplatz nutzt, sind Sie vielleicht versucht, effizient zu sein und Daten als Nebeneffekt der aktuellen Displaygröße zu laden.

Dies widerspricht jedoch dem Prinzip des unidirektionalen Datenflusses, bei dem Daten nach oben verschoben und Composables zur Verfügung gestellt werden können, damit sie entsprechend gerendert werden. Dem Composable sollten genügend Daten zur Verfügung gestellt werden, damit es immer genügend Inhalte für jede Displaygröße hat, auch wenn ein Teil der Inhalte möglicherweise nicht immer verwendet wird.

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

Im Card-Beispiel wird der description immer an den Card übergeben. Auch wenn description nur verwendet wird, wenn die Breite die Darstellung zulässt, ist Card immer erforderlich, unabhängig von der verfügbaren Breite.description

Wenn Sie immer genügend Inhalt übergeben, werden adaptive Layouts einfacher, da sie weniger zustandsbehaftet sind. Außerdem werden Nebeneffekte vermieden, die beim Wechseln zwischen Displaygrößen auftreten können (z. B. durch eine Fensteranpassung, eine Änderung der Ausrichtung oder das Ein- und Ausklappen eines Geräts).

Außerdem kann so der Status bei Layoutänderungen beibehalten werden. Wenn Sie Informationen, die möglicherweise nicht bei allen Anzeigegrößen verwendet werden, nach oben verschieben, können Sie den App-Status beibehalten, wenn sich die Layoutgröße ändert.

Sie können beispielsweise ein boolesches Flag showMore hochziehen, damit der App-Status beibehalten wird, wenn durch die Größenänderung des Displays das Layout zwischen dem Ausblenden und Einblenden von Inhalten wechselt:

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

Weitere Informationen

Weitere Informationen zu adaptiven Layouts in Compose finden Sie in den folgenden Ressourcen:

Beispiel-Apps

  • CanonicalLayouts ist ein Repository mit bewährten Designmustern, die eine optimale Nutzerfreundlichkeit auf großen Displays bieten.
  • JetNews zeigt, wie Sie eine App entwerfen, deren Benutzeroberfläche sich an den verfügbaren Anzeigebereich anpasst.
  • Antworten ist ein adaptives Beispiel für die Unterstützung von Smartphones, Tablets und Faltgeräten.
  • Now in Android ist eine App, die adaptive Layouts verwendet, um verschiedene Displaygrößen zu unterstützen.

Videos