Verschiedene Bildschirmgrößen unterstützen

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

Damit möglichst viele Displaygrößen unterstützt werden – ob unterschiedliche Gerätebildschirme oder unterschiedliche App-Fenster im Mehrfenstermodus –, 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 Splitscreen-Modus und dem Desktop-Freiform-Fenster verwendet werden.

Responsive/adaptive Layouts ändern sich je nach verfügbarem Platz auf dem Display. Die Änderungen reichen von kleinen Layoutanpassungen, die den Platz ausfüllen (responsives Design), bis hin zum vollständigen Ersetzen eines Layouts durch ein anderes, damit deine App am besten für verschiedene Displaygrößen geeignet ist (adaptives Design).

Als deklaratives UI-Toolkit eignet sich Jetpack Compose ideal zum 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

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

Vermeide es, physische Hardwarewerte für Layoutentscheidungen zu verwenden. Es mag verlockend sein, Entscheidungen auf der Grundlage eines festen, greifbaren Werts zu treffen (Ist das Gerät ein Tablet? Hat das physische Display ein bestimmtes Seitenverhältnis?), aber die Antworten auf diese Fragen sind möglicherweise nicht hilfreich, um den für die Benutzeroberfläche verfügbaren Platz zu bestimmen.

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

Auf Tablets kann eine App im Mehrfenstermodus ausgeführt werden. Das bedeutet, dass sich die App den Bildschirm mit einer anderen App teilt. Im Desktop-Freiform-Fenster-Modus oder unter ChromeOS kann sich eine App in einem Fenster mit anpassbarer Größe befinden. Es kann sogar mehr als ein physisches Display geben, z. B. bei einem Foldable. In all diesen Fällen ist die physische Bildschirmgröße nicht relevant für die Entscheidung, wie Inhalte angezeigt werden sollen.

Triff stattdessen Entscheidungen auf Grundlage des tatsächlichen Teils des Bildschirms, der deiner 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 findest du im JetNews-Beispiel.

Wenn Sie Ihre Layouts an den verfügbaren Anzeigebereich anpassen, verringert sich auch der Aufwand für die Unterstützung von Plattformen wie ChromeOS und Formfaktoren wie Tablets und faltbaren Geräten.

Wenn Sie die Messwerte des für Ihre App verfügbaren Bereichs ermittelt haben, konvertieren Sie die Rohgröße in eine Fenstergrößenklasse, wie unter Fenstergrößenklassen verwenden beschrieben. Fenstergrößenklassen sind Haltepunkte, die die Einfachheit der App-Logik mit der Flexibilität zur Optimierung Ihrer App für die meisten Displaygrößen in Einklang bringen.

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 weitergeben oder zusätzliche Logik ausführen, um einen abgeleiteten Status zu erstellen, der an verschachtelte Composables weitergegeben wird.

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).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 wird die Logik für die Displaygröße auf einen einzigen Ort beschränkt, anstatt sie an vielen Stellen in Ihrer App zu verteilen, die synchronisiert werden müssen. An einem einzelnen Ort wird ein Status erzeugt, der wie jeder andere App-Status explizit an andere Composables weitergegeben werden kann. Die explizite Übergabe des Status vereinfacht einzelne Composables, da sie die Fenstergrößenklasse oder die angegebene Konfiguration zusammen mit anderen Daten übernehmen.

Flexible verschachtelte Composables sind wiederverwendbar

Composables sind besser wiederverwendbar, wenn sie an vielen verschiedenen Stellen platziert werden können. Wenn ein Compose-Element an einer bestimmten Stelle 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 zusammensetzbare Funktion vor, die ein Listen-Detailansicht-Layout implementiert, das entweder einen einzelnen Bereich oder zwei Bereiche nebeneinander anzeigen kann:

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

Die Entscheidung für das Listen-Detail-Layout sollte Teil des Gesamtlayouts der App sein. Daher wird die Entscheidung von einer zusammensetzbaren Funktion auf Inhaltsebene weitergegeben:

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

Was ist, wenn Sie stattdessen möchten, dass eine komponierbare Funktion ihr Layout unabhängig vom verfügbaren Anzeigebereich ändert, z. B. eine Karte, die zusätzliche Details anzeigt, wenn der Platz es zulässt? Sie möchten eine Logik basierend auf einer verfügbaren Displaygröße ausführen, aber welche Größe genau?

Abbildung 3. Schmale Karte, auf der nur ein Symbol und ein Titel zu sehen sind, und eine breitere Karte mit Symbol, Titel und Kurzbeschreibung.

Versuchen Sie nicht, die Größe des tatsächlichen Bildschirms des Geräts zu verwenden. Dies ist für verschiedene Bildschirmtypen nicht korrekt und auch nicht, wenn die App nicht im Vollbildmodus ausgeführt wird.

Da die zusammensetzbare Funktion keine zusammensetzbare Funktion auf Inhaltsebene ist, sollten Sie die aktuellen Fenstermesswerte nicht direkt verwenden.

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

Verwenden Sie die Breite, die dem Compose-Element tatsächlich zum Rendern zugewiesen wird. Sie haben zwei Möglichkeiten, diese Breite zu ermitteln:

  • Wenn Sie ändern möchten, wo oder wie Inhalte angezeigt werden, verwenden Sie eine Sammlung von Modifikatoren 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 Messungseinschränkungen, mit denen Sie je nach verfügbarem Anzeigebereich verschiedene Compose-Funktionen aufrufen können. Dies ist jedoch mit gewissen Kosten verbunden, da BoxWithConstraints die Komposition bis zur Layoutphase verzögert, wenn diese Einschränkungen bekannt sind. Dadurch wird während des Layouts mehr Arbeit ausgeführt.

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

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

Wenn Sie ein Composable implementieren, das zusätzlichen Platz auf dem Display nutzt, sind Sie möglicherweise versucht, effizient zu sein und Daten als Nebeneffekt der aktuellen Displaygröße zu laden.

Dies verstößt jedoch gegen das Prinzip des unidirektionalen Datenflusses, bei dem Daten hochgezogen und Composables zur Verfügung gestellt werden können, damit sie entsprechend gerendert werden. Dem Compose-Element 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 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)
            }
        }
    }
}

Beachten Sie im Beispiel Card, dass die description immer an die Card übergeben wird. Obwohl die description nur verwendet wird, wenn die Breite die Anzeige zulässt, ist für Card immer die description erforderlich, unabhängig von der verfügbaren Breite.

Wenn immer genügend Inhalte übergeben werden, werden adaptive Layouts einfacher, da sie weniger zustandsorientiert sind. Außerdem werden Nebenwirkungen beim Wechsel zwischen Displaygrößen vermieden, die durch eine Größenänderung des Fensters, eine Änderung der Ausrichtung oder das Auf- und Zuklappen eines Geräts auftreten können.

Dieses Prinzip ermöglicht es auch, den Status bei Layoutänderungen beizubehalten. Wenn Sie Informationen, die möglicherweise nicht bei allen Displaygröß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 hochstufen, damit der App-Status beibehalten wird, wenn das Layout aufgrund einer Größenänderung des Displays 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 Nutzererfahrung auf großen Displays bieten.
  • JetNews zeigt, wie Sie eine App entwerfen, die ihre Benutzeroberfläche an den verfügbaren Platz auf dem Display anpasst.
  • Reply ist ein adaptives Beispiel für die Unterstützung von Smartphones, Tablets und faltbaren Geräten.
  • Now in Android ist eine App, die adaptive Layouts verwendet, um verschiedene Displaygrößen zu unterstützen.

Videos