Modifikatoren für die Inhaltserstellung

Mit Modifikatoren können Sie ein Composeable verzieren oder ergänzen. Mit Modifikatoren können Sie Folgendes tun:

  • Größe, Layout, Verhalten und Darstellung des Composeables ändern
  • Informationen hinzufügen, z. B. Labels für Bedienungshilfen
  • Nutzereingabe verarbeiten
  • Übergeordnete Interaktionen hinzufügen, z. B. dafür sorgen, dass ein Element anklickbar, scrollbar, ziehbar oder zoombar ist

Modifikatoren sind Kotlin-Standardobjekte. Erstellen Sie einen Modifikator, indem Sie eine der Klassenfunktionen Modifier aufrufen:

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

Zwei Textzeilen auf einem farbigen Hintergrund mit einem Abstand um den Text herum.

Sie können diese Funktionen verketten, um sie zusammenzusetzen:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

Der farbige Hintergrund hinter dem Text erstreckt sich jetzt über die gesamte Breite des Geräts.

Im obigen Code werden verschiedene Modifiziererfunktionen zusammen verwendet.

  • padding fügt einem Element einen Abstand hinzu.
  • Mit fillMaxWidth wird für die zusammensetzbare Funktion die maximale Breite festgelegt, die ihr von ihrem übergeordneten Element zugewiesen wird.

Es wird empfohlen, dass alle Ihre Composeables einen modifier-Parameter akzeptieren und diesen Modifikator an das erste untergeordnete Element weitergeben, das die Benutzeroberfläche ausgibt. Dadurch ist Ihr Code wiederverwendbar und sein Verhalten vorhersehbarer und intuitiver. Weitere Informationen finden Sie in den Richtlinien für die Compose API unter Elemente akzeptieren und respektieren einen Modifikatorparameter.

Reihenfolge der Modifikatoren ist wichtig

Die Reihenfolge der Modifizierfunktionen ist wichtig. Da jede Funktion Änderungen an dem von der vorherigen Funktion zurückgegebenen Modifier vornimmt, wirkt sich die Reihenfolge auf das Endergebnis aus. Sehen wir uns dazu ein Beispiel an:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

Der gesamte Bereich, einschließlich des Abstands an den Rändern, reagiert auf Klicks.

Im Code oben ist der gesamte Bereich anklickbar, einschließlich des umgebenden Abstands, da der Modifikator padding nach dem Modifikator clickable angewendet wurde. Wenn die Reihenfolge der Modifikatoren umgekehrt ist, reagiert der durch padding hinzugefügte Abstand nicht auf Nutzereingaben:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

Der Abstand am Rand des Layouts reagiert nicht mehr auf Klicks

Integrierte Modifikatoren

Jetpack Compose bietet eine Liste vordefinierter Modifikatoren, mit denen Sie ein Compose-Element verzieren oder ergänzen können. Hier sind einige gängige Modifikatoren, mit denen Sie Ihre Layouts anpassen können.

padding und size

Standardmäßig umschließen die untergeordneten Layouts in der Funktion „Schreiben“. Sie können jedoch eine Größe mit dem size-Modifikator festlegen:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

Die von Ihnen angegebene Größe wird möglicherweise nicht berücksichtigt, wenn sie nicht den Einschränkungen des übergeordneten Elements des Layouts entspricht. Wenn die Größe des zusammensetzbaren Elements unabhängig von den eingehenden Einschränkungen festgelegt werden soll, verwenden Sie den Modifikator requiredSize:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

Das untergeordnete Bild ist größer als die Einschränkungen des übergeordneten Elements

In diesem Beispiel hat das Element Image eine Höhe von 150.dp, auch wenn das übergeordnete Element height auf 100.dp festgelegt ist, da der Modifikator requiredSize Vorrang hat.

Wenn ein untergeordnetes Layout die gesamte vom übergeordneten Layout zulässige Höhe einnehmen soll, fügen Sie den Modifikator fillMaxHeight hinzu. Compose bietet auch fillMaxSize und fillMaxWidth:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

Die Bildhöhe ist so groß wie die des übergeordneten Elements.

Wenn Sie allen Seiten eines Elements einen Abstand hinzufügen möchten, legen Sie einen padding-Modifikator fest.

Wenn Sie oberhalb einer Textbasis einen Abstand hinzufügen möchten, um einen bestimmten Abstand vom oberen Rand des Layouts zur Grundlinie zu erreichen, verwenden Sie den Modifikator paddingFromBaseline:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

Text mit einem Abstand darüber

Offset

Wenn Sie ein Layout relativ zu seiner ursprünglichen Position positionieren möchten, fügen Sie den Modifikator offset hinzu und legen Sie den Versatz auf der x- und y-Achse fest. Abweichungen können sowohl positiv als auch negativ sein. Der Unterschied zwischen padding und offset besteht darin, dass durch das Hinzufügen einer offset zu einer zusammensetzbaren Funktion die Messwerte nicht geändert werden:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

Text wird auf die rechte Seite des übergeordneten Containers verschoben

Der Modifikator offset wird horizontal entsprechend der Layoutrichtung angewendet. Bei einem linksläufigen Kontext wird das Element durch eine positive offset nach rechts verschoben, bei einem rechtsläufigen Kontext nach links. Wenn Sie einen Offset festlegen möchten, ohne die Layoutrichtung zu berücksichtigen, sehen Sie sich den Modifikator absoluteOffset an. Bei einem positiven Offsetwert wird das Element immer nach rechts verschoben.

Der Modifikator offset hat zwei Überladungen: offset, bei der die Abweichungen als Parameter übergeben werden, und offset, bei der ein Lambda übergeben wird. Weitere Informationen dazu, wann Sie welche Methode verwenden und wie Sie die Leistung optimieren können, finden Sie im Abschnitt Leistung beim Komponieren – Lesevorgänge so lange wie möglich verschieben.

Gültigkeitsbereichssicherheit in Compose

In Compose gibt es Modifikatoren, die nur auf untergeordnete Elemente bestimmter Composeables angewendet werden können. Compose erzwingt dies mithilfe benutzerdefinierter Bereiche.

Wenn Sie beispielsweise ein untergeordnetes Element so groß wie das übergeordnete Element Box machen möchten, ohne die Größe von Box zu beeinflussen, verwenden Sie den Modifikator matchParentSize. matchParentSize ist nur in BoxScope verfügbar. Daher kann es nur für ein untergeordnetes Element innerhalb eines übergeordneten Box-Elements verwendet werden.

Mit dem Bereichsschutz können Sie keine Modifikatoren hinzufügen, die in anderen Composeable-Objekten und Bereichen nicht funktionieren. Außerdem sparen Sie Zeit, da Sie nicht durch Ausprobieren herausfinden müssen, welche Modifikatoren funktionieren.

Mit auf einen Bereich reduzierten Modifikatoren wird das übergeordnete Element über einige Informationen informiert, die es über das untergeordnete Element wissen sollte. Sie werden auch als Modifizierer für übergeordnete Daten bezeichnet. Ihre internen Funktionen unterscheiden sich von den Modifikatoren für allgemeine Zwecke, aber aus Sicht der Verwendung spielen diese Unterschiede keine Rolle.

matchParentSize in Box

Wie bereits erwähnt, verwenden Sie den Modifikator matchParentSize, wenn ein untergeordnetes Layout dieselbe Größe wie ein übergeordnetes Box haben soll, ohne dass sich die Größe von Box ändert.

matchParentSize ist nur in einem Box-Bereich verfügbar, d. h., es gilt nur für direkte untergeordnete Elemente von Box-Zusammensetzbaren.

Im folgenden Beispiel übernimmt das untergeordnete Element Spacer seine Größe von seinem übergeordneten Box, das wiederum die Größe von den größten untergeordneten Elementen, in diesem Fall ArtistCard, entnimmt.

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

Grauer Hintergrund, der den Container füllt

Wenn fillMaxSize anstelle von matchParentSize verwendet würde, würde Spacer den gesamten verfügbaren Speicherplatz belegen, der dem übergeordneten Element zur Verfügung steht, wodurch wiederum das übergeordnete Element erweitert wird und den gesamten verfügbaren Bereich ausfüllt.

Grauer Hintergrund, der den gesamten Bildschirm ausfüllt

weight in Row und Column

Wie Sie im vorherigen Abschnitt unter Abstand und Größe gesehen haben, wird eine zusammensetzbare Größe standardmäßig durch den Inhalt definiert, der umschlossen wird. Mit dem weight-Modifikator, der nur in RowScope und ColumnScope verfügbar ist, können Sie die Größe eines Elements im übergeordneten Element flexibel festlegen.

Nehmen wir als Beispiel eine Row mit zwei Box-Kompositionen. Das erste Feld hat doppelt so viel weight wie das zweite, also doppelt so viel Breite. Da Row 210.dp breit ist, ist der erste Box 140.dp breit und der zweite 70.dp:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

Die Bildbreite ist doppelt so groß wie die Textbreite.

Modifikatoren extrahieren und wiederverwenden

Mehrere Modifikatoren können miteinander verknüpft werden, um ein Composeable zu verzieren oder zu ergänzen. Diese Kette wird über die Modifier-Schnittstelle erstellt, die eine sortierte, unveränderliche Liste einzelner Modifier.Elements darstellt.

Jede Modifier.Element steht für ein individuelles Verhalten, z. B. Layout, Zeichen- und Grafikverhalten, alle gesten-, Fokus- und semantischen Verhaltensweisen sowie Geräteeingabeereignisse. Die Reihenfolge ist wichtig: Modifizierelemente, die zuerst hinzugefügt werden, werden zuerst angewendet.

Manchmal kann es vorteilhaft sein, dieselben Modifikatorketteninstanzen in mehreren Composeable-Objekten wiederzuverwenden. Dazu werden sie in Variablen extrahiert und in einen höheren Gültigkeitsbereich verschoben. Dies kann aus mehreren Gründen dazu beitragen, die Lesbarkeit des Codes oder die Leistung Ihrer App zu verbessern:

  • Die Neuzuweisung der Modifikatoren wird nicht wiederholt, wenn für Composables, in denen sie verwendet werden, eine Neuzusammensetzung erfolgt.
  • Modifikatorketten können potenziell sehr lang und komplex sein. Wenn Sie dieselbe Instanz einer Kette wiederverwenden, kann die Arbeitslast der Compose-Laufzeit beim Vergleichen verringert werden.
  • Diese Extraktion trägt zu einem sauberen, einheitlichen und wartungsfreundlichen Code in der gesamten Codebasis bei.

Best Practices für die Wiederverwendung von Modifikatoren

Erstellen Sie eigene Modifier-Ketten und extrahieren Sie sie, um sie für mehrere zusammensetzbare Komponenten wiederzuverwenden. Es ist völlig in Ordnung, nur einen Modifikator zu speichern, da es sich um datenähnliche Objekte handelt:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

Modifikatoren bei häufig wechselndem Status extrahieren und wiederverwenden

Wenn sich die Status in Composables häufig ändern, z. B. Animationen oder scrollState, kann es zu einer erheblichen Anzahl von Neuzusammensetzungen kommen. In diesem Fall werden Ihre Modifier bei jeder Neuzusammensetzung und potenziell für jeden Frame zugewiesen:

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

Stattdessen können Sie dieselbe Instanz des Modifikators erstellen, extrahieren, wiederverwenden und wie folgt an die zusammensetzbare Funktion übergeben:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

Modifikatoren ohne Bereich extrahieren und wiederverwenden

Modifikatoren können ohne oder mit Gültigkeitsbereich für ein bestimmtes Kompositionalobjekt festgelegt werden. Modifier ohne Bereich können Sie ganz einfach außerhalb von Composables als einfache Variablen extrahieren:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

Das ist besonders in Kombination mit Lazy-Layouts von Vorteil. In den meisten Fällen sollten alle Ihre Artikel, die möglicherweise sehr zahlreich sind, genau dieselben Modifikatoren haben:

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

Modifikatoren mit Bereich extrahieren und wiederverwenden

Wenn Sie mit Modifikatoren arbeiten, die auf bestimmte zusammensetzbare Funktionen beschränkt sind, können Sie diese auf die höchstmögliche Ebene extrahieren und gegebenenfalls wiederverwenden:

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

Die extrahierten, auf einen bestimmten Bereich bezogenen Modifikatoren sollten nur an die direkten untergeordneten Elemente mit demselben Bereich übergeben werden. Weitere Informationen dazu, warum das wichtig ist, finden Sie im Abschnitt Sicherheitsbereich in Compose:

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

Weitere Verknüpfung der extrahierten Modifikatoren

Sie können die extrahierten Modifikatorketten weiter verketten oder anhängen, indem Sie die Funktion .then() aufrufen:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

Denken Sie daran, dass die Reihenfolge der Modifikatoren wichtig ist.

Weitere Informationen

Eine vollständige Liste der Modifikatoren mit ihren Parametern und Bereichen finden Sie in diesem Artikel.

Weitere Übungen zur Verwendung von Modifikatoren finden Sie im Codelab zu grundlegenden Layouts in Compose oder im Now in Android-Repository.

Weitere Informationen zu benutzerdefinierten Modifikatoren und deren Erstellung finden Sie in der Dokumentation unter Benutzerdefinierte Layouts – Layout-Modifikator verwenden.