Modifikatoren für die Inhaltserstellung

Mithilfe von Modifikatoren können Sie zusammensetzbare Funktionen dekorieren oder erweitern. Modifikatoren ermöglichen Folgendes:

  • Größe, Layout, Verhalten und Aussehen der zusammensetzbaren Funktion ä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 Modifier-Klassenfunktionen aufrufen:

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

Zwei Textzeilen auf farbigem Hintergrund mit Innenabständen.

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 dehnt sich nun auf die gesamte Breite des Geräts aus.

Im obigen Code werden verschiedene Modifikatorfunktionen zusammen verwendet.

  • padding platziert ein Element mit Platz.
  • Mit fillMaxWidth wird für die zusammensetzbare Funktion die maximale Breite festgelegt, die ihr von ihrem übergeordneten Element zugewiesen wird.

Als Best Practice wird empfohlen, dass alle zusammensetzbaren Funktionen einen modifier-Parameter akzeptieren und diesen Modifikator an das erste untergeordnete Element übergeben, das UI 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 Modifier-Parameter akzeptieren und respektieren.

Die Reihenfolge der Modifikatoren ist wichtig

Die Reihenfolge der Modifikatorfunktionen ist erheblich. Da jede Funktion Änderungen an der Modifier vornimmt, die von der vorherigen Funktion zurückgegeben wurde, wirkt sich die Sequenz 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 darüber ist der gesamte Bereich anklickbar, einschließlich des umgebenden Rahmens, da der padding-Modifikator nach dem clickable-Modifikator angewendet wurde. Wenn die Reihenfolge der Modifikatoren umgekehrt ist, reagiert das von padding hinzugefügte Leerzeichen 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 eine zusammensetzbare Funktion dekorieren oder erweitern 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 { /*...*/ }
    }
}

Beachten Sie, dass die von Ihnen angegebene Größe möglicherweise nicht berücksichtigt wird, wenn sie die Einschränkungen des übergeordneten Elements des Layouts nicht erfüllt. Wenn die zusammensetzbare Größe ungeachtet der eingehenden Einschränkungen unveränderlich sein muss, 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 Image ist größer als die Einschränkungen des übergeordneten Images

Selbst wenn das übergeordnete height-Element auf 100.dp festgelegt ist, ist in diesem Beispiel die Höhe von Image 150.dp, da der Modifikator requiredSize Vorrang hat.

Wenn ein untergeordnetes Layout die gesamte verfügbare Höhe ausfüllen soll, die vom übergeordneten Element zulässig ist, fügen Sie den Modifikator fillMaxHeight hinzu (Compose bietet außerdem 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 ist so groß wie das übergeordnete Bild.

Wenn Sie ein Element vollständig mit Innenrand versehen 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 Abstand darüber

Abweichung

Wenn Sie ein Layout relativ zur ursprünglichen Position positionieren möchten, fügen Sie den Modifikator offset hinzu und legen Sie den Versatz in der x- und y-Achse fest. Offsets können sowohl positiv als auch nicht positiv 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 wurde auf die rechte Seite des übergeordneten Containers verschoben

Der offset-Modifikator wird horizontal entsprechend der Layoutrichtung angewendet. In einem rechtsläufigen Kontext verschiebt eine positive offset das Element nach rechts, während es das Element in einem linksläufigen Kontext nach links verschiebt. Wenn Sie einen Versatz festlegen müssen, ohne die Layoutrichtung zu berücksichtigen, verwenden Sie den Modifikator absoluteOffset, mit dem ein positiver Versatzwert das Element immer nach rechts verschiebt.

Der offset-Modifikator bietet zwei Überlastungen: offset, die die Offsets als Parameter verwenden, und offset, die eine Lambda-Funktion übernimmt. Detailliertere Informationen zur jeweiligen Verwendung und zur Leistungsoptimierung finden Sie im Abschnitt Compose performance – Defer read as available.

Bereichssicherheit in Compose

In Compose gibt es Modifikatoren, die nur verwendet werden können, wenn sie auf untergeordnete Elemente bestimmter zusammensetzbarer Funktionen angewendet werden. Durch das Schreiben wird dies mithilfe von benutzerdefinierten Bereichen erzwungen.

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 Modifizierer matchParentSize. matchParentSize ist nur in BoxScope verfügbar. Daher kann es nur für ein untergeordnetes Element innerhalb eines übergeordneten Box-Elements verwendet werden.

Die Bereichssicherheit verhindert, dass Sie Modifikatoren hinzufügen, die in anderen zusammensetzbaren Funktionen und Bereichen nicht funktionieren würden, und spart Zeit durch Ausprobieren.

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

matchParentSize in Box

Wenn Sie möchten, dass ein untergeordnetes Layout dieselbe Größe wie ein übergeordnetes Box hat, ohne die Größe von Box zu beeinflussen, verwenden Sie den Modifikator matchParentSize, wie oben erwähnt.

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()
    }
}

Behälter mit grauem Hintergrund

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 füllt den Bildschirm

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 eine zusammensetzbare Größe innerhalb des übergeordneten Elements flexibel festlegen.

Nehmen wir ein Row, das zwei zusammensetzbare Box-Elemente enthält. Das erste Feld wird doppelt so groß wie das weight des zweiten Felds, also doppelt so breit. 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)
        ) {
            /*...*/
        }
    }
}

Das Bild entspricht der doppelten Textbreite

Modifikatoren extrahieren und wiederverwenden

Mehrere Modifikatoren können miteinander verkettet werden, um eine zusammensetzbare Funktion zu dekorieren oder zu erweitern. Diese Kette wird über die Schnittstelle Modifier erstellt, die eine geordnete, 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. Ihre Reihenfolge ist wichtig: Modifikatorelemente, die zuerst hinzugefügt werden, werden zuerst angewendet.

Manchmal kann es vorteilhaft sein, dieselben Modifikatorketteninstanzen in mehreren zusammensetzbaren Funktionen wiederzuverwenden, indem Sie sie in Variablen extrahieren und in höheren Bereichen hochstufen. Sie kann die Lesbarkeit von Code oder die Leistung Ihrer Anwendung aus verschiedenen Gründen verbessern:

  • Die Neuzuweisung der Modifikatoren erfolgt nicht, wenn zusammensetzbare Funktionen neu zusammengesetzt werden, in denen sie verwendet werden.
  • Modifikatorketten können potenziell sehr lang und komplex sein. Durch die Wiederverwendung derselben Instanz einer Kette kann sich die Arbeitslast verringern, die die Compose-Laufzeit beim Vergleich leisten muss.
  • Diese Extraktion fördert die Codereinheit, Konsistenz und Verwaltbarkeit in der gesamten Codebasis.

Best Practices für die Wiederverwendung von Modifizierern

Erstellen Sie Ihre eigenen Modifier-Ketten und extrahieren Sie sie, um sie in mehreren zusammensetzbaren 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)

Modifizierer extrahieren und wiederverwenden, wenn sich der Zustand häufig ändert

Bei der Beobachtung von sich häufig ändernden Status in zusammensetzbaren Funktionen, wie Animationsstatus oder scrollState, kann eine erhebliche Anzahl von Neuzusammensetzungen erfolgen. In diesem Fall werden Ihre Modifikatoren jeder Neuzusammensetzung und möglicherweise auch jedem 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 Geltungsbereich extrahieren und wiederverwenden

Modifikatoren können ohne Beschränkung oder auf eine bestimmte zusammensetzbare Funktion beschränkt werden. Modifikatoren ohne Bereichsangabe können Sie ganz einfach als einfache Variablen aus zusammensetzbaren Funktionen 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 soll es sinnvoll sein, dass alle Ihre potenziell signifikante Anzahl von Elementen exakt 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,
            )
        }
    }
}

Bereichsmodifikatoren 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
        // ...
    )
    // ...
}

Sie sollten die extrahierten Bereichsmodifikatoren nur an die direkt untergeordneten untergeordneten Elemente mit demselben Umfang übergeben. Weitere Informationen dazu, warum das wichtig ist, finden Sie im Abschnitt Bereichssicherheit 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 Modifikatorketten durch Aufrufen der Funktion .then() weiter verketten oder anhängen:

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

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

// Append your reusableModifier
otherModifier.then(reusableModifier)

Beachte dabei aber, dass die Reihenfolge der Modifikatoren wichtig ist.

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 deren Erstellung finden Sie in der Dokumentation zu Benutzerdefinierte Layouts – Layout-Modifikator verwenden.