Modifikatoren für die Inhaltserstellung

Mit Modifikatoren können Sie ein Composable-Element dekorieren oder erweitern. Mit Modifikatoren können Sie beispielsweise Folgendes tun:

  • Größe, Layout, Verhalten und Darstellung der Composable-Funktion ändern
  • Informationen wie Labels für Barrierefreiheit hinzufügen
  • Nutzereingabe verarbeiten
  • Allgemeine Interaktionen hinzufügen, z. B. ein Element anklickbar, scrollbar, ziehbar oder zoombar machen

Modifikatoren sind Standard-Kotlin-Objekte. Erstellen Sie einen Modifier, indem Sie eine der Funktionen der Klasse 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 Innenabstand um den Text.

Sie können diese Funktionen verketten, um sie zu kombinieren:

@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 Modifikatorfunktionen zusammen verwendet.

  • Mit padding wird um ein Element herum Platz geschaffen.
  • Mit fillMaxWidth wird festgelegt, dass das Composable die maximale Breite einnimmt, die ihm vom übergeordneten Element zugewiesen wird.

Es wird empfohlen, dass alle Ihre Composables einen modifier-Parameter akzeptieren und diesen Modifier an das erste untergeordnete Element übergeben, das UI ausgibt. Dadurch wird Ihr Code wiederverwendbarer und sein Verhalten vorhersehbarer und intuitiver. Weitere Informationen finden Sie in den Compose API-Richtlinien unter Elements accept and respect a Modifier parameter (Elemente akzeptieren und berücksichtigen einen Modifier-Parameter).

Reihenfolge der Modifikatoren ist wichtig

Die Reihenfolge der Änderungsfunktionen ist wichtig. Da jede Funktion Änderungen an der Modifier vornimmt, die von der vorherigen Funktion zurückgegeben wurde, wirkt sich die Reihenfolge auf das Endergebnis aus. Sehen wir uns 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 Innenabstands an den Rändern, reagiert auf Klicks.

Im obigen Code ist der gesamte Bereich klickbar, einschließlich des umgebenden Abstands, da der padding-Modifikator nach dem clickable-Modifikator angewendet wurde. Wenn die Reihenfolge der Modifizierer umgekehrt wird, reagiert der von padding hinzugefügte Leerraum 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 integrierter Modifikatoren, mit denen Sie ein Composable-Element dekorieren oder erweitern können. Hier sind einige häufig verwendete Modifikatoren, mit denen Sie Ihre Layouts anpassen können.

padding und size

Standardmäßig umschließen in Compose bereitgestellte Layouts ihre untergeordneten Elemente. Sie können jedoch eine Größe mit dem Modifikator size festlegen:

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

Die angegebene Größe wird möglicherweise nicht berücksichtigt, wenn sie nicht den Einschränkungen des übergeordneten Layouts entspricht. Wenn die Größe der zusammensetzbaren Funktion unabhängig von den eingehenden Einschränkungen festgelegt werden soll, verwenden Sie den Modifier 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 Bildes.

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

Wenn ein untergeordnetes Layout die gesamte vom übergeordneten Element zulässige Höhe einnehmen soll, fügen Sie den Modifier 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 Höhe des Bildes entspricht der Höhe des übergeordneten Elements.

Wenn Sie ein Element mit Innenabstand umgeben möchten, legen Sie den Modifikator padding fest.

Wenn Sie über einer Text-Baseline einen Innenabstand hinzufügen möchten, um einen bestimmten Abstand vom oberen Rand des Layouts zur Baseline zu erzielen, 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 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 Offset auf der x- und y-Achse fest. Offsets können positiv oder nicht positiv sein. Der Unterschied zwischen padding und offset besteht darin, dass das Hinzufügen von offset zu einem Composable seine Abmessungen nicht ändert:

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

Text nach rechts vom übergeordneten Container verschoben

Der Modifikator offset wird horizontal entsprechend der Layoutrichtung angewendet. In einem von links nach rechts ausgerichteten Kontext verschiebt ein positiver offset-Wert das Element nach rechts, in einem von rechts nach links ausgerichteten Kontext nach links. Wenn Sie einen Offset festlegen möchten, ohne die Layoutrichtung zu berücksichtigen, sehen Sie sich den Modifier absoluteOffset an. Dort wird das Element bei einem positiven Offsetwert immer nach rechts verschoben.

Der Modifikator offset bietet zwei Überladungen: offset, die die Offsets als Parameter verwendet, und offset, die eine Lambda-Funktion verwendet. Ausführlichere Informationen dazu, wann die einzelnen Optionen verwendet werden sollten und wie Sie die Leistung optimieren können, finden Sie im Abschnitt Compose-Leistung – Lesevorgänge so lange wie möglich aufschieben.

Sicherheit von Bereichen in Compose

In Compose gibt es Modifikatoren, die nur verwendet werden können, wenn sie auf untergeordnete Elemente bestimmter Composables angewendet werden. In Compose wird dies durch benutzerdefinierte Bereiche erzwungen.

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

Durch die Bereichssicherheit wird verhindert, dass Sie Modifikatoren hinzufügen, die in anderen Composables und Bereichen nicht funktionieren. So sparen Sie Zeit, die Sie sonst für das Ausprobieren verschiedener Optionen benötigen würden.

Mit bereichsbezogenen Modifikatoren werden Eltern über bestimmte Informationen benachrichtigt, die sie über ihr Kind wissen sollten. Sie werden auch häufig als übergeordnete Datenmodifikatoren bezeichnet. Die internen Abläufe unterscheiden sich von denen der Allzweckmodifikatoren, aber aus Nutzersicht spielen diese Unterschiede keine Rolle.

matchParentSize in Box

Wie oben erwähnt, können Sie mit dem Modifikator matchParentSize ein untergeordnetes Layout auf die Größe eines übergeordneten Box festlegen, ohne die Größe des Box zu ändern.

Beachten Sie, dass matchParentSize nur innerhalb eines Box-Bereichs verfügbar ist. Das bedeutet, dass es nur für direkte untergeordnete Elemente von Box-Composables gilt.

Im Beispiel unten übernimmt das untergeordnete Element Spacer seine Größe vom übergeordneten Element Box, das wiederum seine Größe vom größten untergeordneten Element, in diesem Fall ArtistCard, übernimmt.

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

Grauer Hintergrund, der den Container ausfüllt

Wenn fillMaxSize anstelle von matchParentSize verwendet würde, würde Spacer den gesamten verfügbaren Platz einnehmen, der dem übergeordneten Element zugewiesen ist. Dadurch würde sich das übergeordnete Element ausdehnen und den gesamten verfügbaren Platz einnehmen.

Grauer Hintergrund, der den Bildschirm ausfüllt

weight in Row und Column

Wie Sie im vorherigen Abschnitt zu Abstand und Größe gesehen haben, wird die Größe einer Composable-Funktion standardmäßig durch den Inhalt definiert, den sie umschließt. Mit dem weight-Modifier, der nur in RowScope und ColumnScope verfügbar ist, können Sie die Größe einer Composable-Funktion so festlegen, dass sie innerhalb des übergeordneten Elements flexibel ist.

Sehen wir uns ein Row an, das zwei Box-Composables enthält. Das erste Feld hat den doppelten weight des zweiten Felds und ist daher doppelt so breit. Da die Row 210.dp breit ist, ist die erste Box 140.dp breit und die 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 Modifier können verkettet werden, um ein Composable zu gestalten oder zu erweitern. Diese Kette wird über die Modifier-Schnittstelle erstellt, die eine geordnete, unveränderliche Liste einzelner Modifier.Elements darstellt.

Jedes Modifier.Element steht für ein einzelnes Verhalten, z. B. Layout-, Zeichen- und Grafikverhalten, alle gestenbezogenen Verhaltensweisen, Fokus- und Semantikverhalten sowie Geräteeingabeereignisse. Die Reihenfolge ist wichtig: Modifikatorelemente, die zuerst hinzugefügt werden, werden auch zuerst angewendet.

Manchmal kann es sinnvoll sein, dieselben Modifier-Ketteninstanzen in mehreren Composables wiederzuverwenden, indem Sie sie in Variablen extrahieren und in höhere Bereiche verschieben. Das kann aus mehreren Gründen die Lesbarkeit von Code verbessern oder die Leistung Ihrer App steigern:

  • Die Neuzuweisung der Modifikatoren wird bei der Neuzusammenstellung von Composables, die sie verwenden, nicht wiederholt.
  • Modifikator-Chains können potenziell sehr lang und komplex sein. Wenn Sie dieselbe Instanz einer Chain wiederverwenden, kann die Compose-Laufzeit die Arbeitslast beim Vergleichen reduzieren.
  • Diese Extraktion fördert die Übersichtlichkeit, Konsistenz und Wartungsfreundlichkeit des Codes in der gesamten Codebasis.

Best Practices für die Wiederverwendung von Modifikatoren

Erstellen Sie eigene Modifier-Ketten und extrahieren Sie sie, um sie in mehreren zusammensetzbaren Komponenten wiederzuverwenden. Es ist völlig in Ordnung, nur einen Modifier zu speichern, da es sich dabei um datenähnliche Objekte handelt:

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

Modifikatoren extrahieren und wiederverwenden, wenn sich der Status häufig ändert

Wenn sich der Status in Composables häufig ändert, z. B. bei Animationsstatus oder scrollState, kann es zu einer erheblichen Anzahl von Neukompositionen kommen. In diesem Fall werden Ihre Modifikatoren bei jeder Neuzusammenstellung und möglicherweise 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 und wiederverwenden und sie so 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
    )
}

Nicht bereichsbezogene Modifikatoren extrahieren und wiederverwenden

Modifikatoren können entweder nicht oder auf eine bestimmte Komponente beschränkt sein. Bei Modifikatoren ohne Bereich können Sie sie ganz einfach als einfache Variablen außerhalb von Composables extrahieren:

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

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

Das kann besonders in Kombination mit Lazy Layouts von Vorteil sein. In den meisten Fällen sollten alle Ihre Artikel, die möglicherweise eine erhebliche Menge ausmachen, 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,
            )
        }
    }
}

Bereichsbezogene Modifikatoren extrahieren und wiederverwenden

Wenn Sie es mit Modifikatoren zu tun haben, die auf bestimmte Composables beschränkt sind, können Sie sie auf die höchstmögliche Ebene extrahieren und bei Bedarf 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
        // ...
    )
    // ...
}

Sie sollten die extrahierten, bereichsbezogenen Modifizierer nur an die direkten untergeordneten Elemente mit demselben Bereich übergeben. Weitere Informationen dazu, warum das wichtig ist, finden Sie im Abschnitt Umfangssicherheit 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 Verkettung extrahierter Modifikatoren

Sie können die extrahierten Modifikatorfolgen 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)

Die Reihenfolge der Modifikatoren ist wichtig.

Weitere Informationen

Hier finden Sie eine vollständige Liste der Modifikatoren mit ihren Parametern und Bereichen.

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

Weitere Informationen zu benutzerdefinierten Modifikatoren und dazu, wie Sie sie erstellen, finden Sie in der Dokumentation unter Benutzerdefinierte Layouts – Layoutmodifikator verwenden.