Grundlagen zum Verfassen von Layouts

Jetpack Compose vereinfacht das Entwerfen und Erstellen der Benutzeroberfläche Ihrer Anwendung. Compose wandelt den Status über einen der folgenden Schritte in UI-Elemente um:

  1. Zusammensetzung der Elemente
  2. Layout der Elemente
  3. Zeichnung von Elementen

Transformieren des Zustands in UI durch Zusammensetzung, Layout oder Zeichnung erstellen

In diesem Dokument wird das Layout von Elementen erläutert. Außerdem werden einige der Bausteine erläutert, die in der Funktion „Compose“ für das Layout Ihrer UI-Elemente verfügbar sind.

Ziele von Layouts in der Funktion "Compose"

Die Jetpack Compose-Implementierung des Layoutsystems hat zwei Hauptziele:

Grundlagen zu zusammensetzbaren Funktionen

Zusammensetzbare Funktionen sind der grundlegende Baustein der Funktion „Compose“. Eine zusammensetzbare Funktion ist eine Funktion, die Unit ausgibt und einen Teil Ihrer UI beschreibt. Die Funktion generiert einige Eingaben und generiert das, was auf dem Bildschirm angezeigt wird. Weitere Informationen zu zusammensetzbaren Funktionen finden Sie in der Dokumentation zum Meditativen Modell erstellen.

Eine zusammensetzbare Funktion kann mehrere UI-Elemente ausgeben. Wenn Sie jedoch keine Anleitung zur Anordnung nennen, können die Elemente in der Funktion „Compose“ auf unpassende Weise angeordnet werden. Mit diesem Code werden beispielsweise zwei Textelemente generiert:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

Ohne Anleitung werden die Textelemente übereinander gestapelt, sodass sie unlesbar werden:

Zwei übereinander gezogene Textelemente, wodurch der Text unlesbar wird

Compose bietet eine Sammlung gebrauchsfertiger Layouts, mit denen Sie Ihre UI-Elemente anordnen können. Außerdem können Sie damit ganz einfach eigene, spezifischere Layouts definieren.

Komponenten für das Standardlayout

In vielen Fällen können Sie einfach die Standard-Layoutelemente von Composer verwenden.

Mit Column können Sie Elemente vertikal auf dem Bildschirm platzieren.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

Zwei Textelemente, die in einem Spaltenlayout angeordnet sind, sodass der Text lesbar ist

Entsprechend kannst du Row verwenden, um Elemente horizontal auf dem Bildschirm zu platzieren. Sowohl Column als auch Row unterstützen die Konfiguration der Ausrichtung der darin enthaltenen Elemente.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

Zeigt ein komplexeres Layout mit einer kleinen Grafik neben einer Spalte mit Textelementen

Mit Box können Sie Elemente übereinander legen. Box unterstützt auch die Konfiguration einer spezifischen Ausrichtung der darin enthaltenen Elemente.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

Zeigt zwei übereinander gestapelte Elemente

Oft sind diese Bausteine alles, was Sie brauchen. Sie können Ihre eigene zusammensetzbare Funktion schreiben, um diese Layouts zu einem ausgefeilteren Layout zu kombinieren, das zu Ihrer App passt.

Vergleicht drei zusammensetzbare Komponenten im einfachen Layout: Spalte, Zeile und Feld.

Wenn Sie die Position von Kindern in einem Row festlegen möchten, verwenden Sie die Argumente horizontalArrangement und verticalAlignment. Legen Sie für Column die Argumente verticalArrangement und horizontalAlignment fest:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

Die Elemente sind rechtsbündig ausgerichtet

Layoutmodell

Im Layoutmodell wird die UI-Struktur in einer einzelnen Karte bzw. einem einzelnen Ticket angelegt. Jeder Knoten wird zuerst dazu aufgefordert, sich selbst zu messen und dann alle untergeordneten Elemente rekursiv zu messen. Dabei werden Größeneinschränkungen des Baums an die untergeordneten Elemente weitergegeben. Anschließend werden die Blattknoten in der Größe angepasst und platziert, wobei die aufgelösten Größen und Platzierungsanweisungen an den Baum übergeben werden.

Kurz gesagt: Eltern messen vor ihren Kindern, werden aber entsprechend der Größe und nach den Kindern platziert.

Betrachten Sie die folgende SearchResult-Funktion.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

Diese Funktion liefert den folgenden UI-Baum.

SearchResult
  Row
    Image
    Column
      Text
      Text

Im SearchResult-Beispiel folgt das Baumlayout der UI in dieser Reihenfolge:

  1. Der Stammknoten Row wird zu einer Messung aufgefordert.
  2. Der Stammknoten Row bittet seinen ersten untergeordneten Knoten, Image, zu messen.
  3. Image ist ein Blattknoten, d. h., er hat keine untergeordneten Elemente. Daher wird eine Größe angegeben und eine Platzierungsanleitung zurückgegeben.
  4. Der Stammknoten Row fordert seinen zweiten untergeordneten Knoten, Column, die Messung an.
  5. Der Knoten Column fordert seinen ersten Text-untergeordneten Knoten an, eine Messung durchzuführen.
  6. Der erste Text-Knoten ist ein Blattknoten, daher meldet er eine Größe und gibt Anleitung zur Platzierung zurück.
  7. Der Knoten Column fordert seinen zweiten Text-untergeordneten Knoten an, eine Messung durchzuführen.
  8. Der zweite Text-Knoten ist ein Blattknoten, daher meldet er eine Größe und gibt Anleitung zur Platzierung zurück.
  9. Nachdem der Column-Knoten nun seine untergeordneten Elemente gemessen, bemessen und platziert hat, kann er seine eigene Größe und Platzierung bestimmen.
  10. Nachdem der Stammknoten Row nun seine untergeordneten Elemente gemessen, skaliert und platziert hat, kann er seine eigene Größe und Position bestimmen.

Sortierung von Messungen, Größen und Platzierung in der Benutzeroberfläche für Suchergebnisse

Leistung

Mit der Funktion „Compose“ lässt sich eine hohe Leistung erzielen, da Kinder nur einmal gemessen werden. Die Messung mit nur einem Durchlauf verbessert die Leistung und ermöglicht es Compose, tiefe UI-Bäume effizient zu verarbeiten. Wenn ein Element sein untergeordnetes Element zweimal gemessen hat und dieses Kind jedes seiner untergeordneten Elemente zweimal gemessen hat, wäre ein einziger Versuch, das Layout einer gesamten UI zu erstellen, viel Arbeit erfordern, was die Leistung der App erschweren würde.

Wenn für Ihr Layout aus irgendeinem Grund mehrere Messungen erforderlich sind, bietet Compose ein spezielles System, intrinsische Messungen. Weitere Informationen zu dieser Funktion finden Sie unter Intrinsische Messungen in Compose-Layouts.

Da Messung und Platzierung unterschiedliche Teilphasen des Layoutpasses sind, können alle Änderungen, die sich nur auf die Platzierung der Elemente und nicht auf die Messung auswirken, separat ausgeführt werden.

Modifikatoren in Layouts verwenden

Wie im Abschnitt Modifikatoren zum Verfassen beschrieben, können Sie Ihre zusammensetzbaren Funktionen mit Modifikatoren gestalten oder erweitern. Modifikatoren sind wichtig, um Ihr Layout anzupassen. Hier verketten wir beispielsweise mehrere Modifikatoren, um ArtistCard anzupassen:

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

Ein noch komplexeres Layout, bei dem mithilfe von Modifikatoren die Anordnung der Grafiken geändert wird und die Bereiche auf Nutzereingaben reagieren.

Beachten Sie, dass im obigen Code verschiedene Modifikatorfunktionen zusammen verwendet werden.

  • clickable erstellt eine zusammensetzbare Reaktion auf Nutzereingabe und zeigt eine Welle.
  • padding fügt Raum um ein Element hinzu.
  • Mit fillMaxWidth füllt die zusammensetzbare Funktion die maximale Breite aus, die ihr von ihrem übergeordneten Element vorgegeben wird.
  • size() gibt die bevorzugte Breite und Höhe eines Elements an.

Scrollbare Layouts

Weitere Informationen zu scrollbaren Layouts finden Sie in der Dokumentation zu Schreibbewegungen.

Informationen zu Listen und Lazy-Listen finden Sie in der Dokumentation zum Erstellen von Listen.

Responsive Layouts

Ein Layout sollte unter Berücksichtigung verschiedener Bildschirmausrichtungen und Formfaktorgrößen entworfen werden. Compose bietet vorkonfigurierte Mechanismen, mit denen Sie Ihre zusammensetzbaren Layouts an verschiedene Bildschirmkonfigurationen anpassen können.

Einschränkungen

Sie können BoxWithConstraints verwenden, um die Einschränkungen der übergeordneten Ressource zu kennen und das Layout entsprechend zu entwerfen. Die Einschränkungen für die Messung sind im Bereich der inhaltsbezogenen Lambda-Funktion zu finden. Mit diesen Einschränkungen für Messungen können Sie verschiedene Layouts für verschiedene Bildschirmkonfigurationen erstellen:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

Slotbasierte Layouts

Compose bietet eine große Auswahl von zusammensetzbaren Funktionen, die auf Material Design mit der Abhängigkeit androidx.compose.material:material basieren (die beim Erstellen eines Composer-Projekts in Android Studio enthalten ist), um das Erstellen einer UI zu vereinfachen. Elemente wie Drawer, FloatingActionButton und TopAppBar sind alle vorhanden.

Für Materialkomponenten werden in großem Umfang Slot-APIs genutzt. Mit dem in der Funktion „Compose“ eingeführten Muster lassen sich zusammensetzbare Funktionen noch individueller gestalten. Auf diese Weise sind die Komponenten flexibler, da sie ein untergeordnetes Element akzeptieren, das sich selbst konfigurieren kann, anstatt jeden Konfigurationsparameter des untergeordneten Elements verfügbar zu machen. Slots hinterlassen in der Benutzeroberfläche einen leeren Bereich, den der Entwickler nach Belieben füllen kann. Sie können beispielsweise die folgenden Slots in einem TopAppBar anpassen:

Ein Diagramm, das die verfügbaren Slots in einer App-Leiste für Material Components zeigt

Zusammensetzbare Funktionen verwenden in der Regel eine zusammensetzbare content-Lambda-Funktion ( content: @Composable () -> Unit). Slot-APIs stellen mehrere content-Parameter für bestimmte Anwendungsfälle bereit. Mit TopAppBar können Sie beispielsweise die Inhalte für title, navigationIcon und actions bereitstellen.

Mit Scaffold kannst du beispielsweise eine UI mit der grundlegenden Material Design-Layoutstruktur implementieren. ScaffoldEs bietet Slots für die gängigsten Material-Komponenten der obersten Ebene, z. B. TopAppBar, BottomAppBar, FloatingActionButton und Drawer. Mit Scaffold kann ganz einfach sichergestellt werden, dass diese Komponenten richtig positioniert sind und richtig zusammenwirken.

Die JetNews-Beispiel-App, die mithilfe von Scaffold mehrere Elemente positioniert

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}