Modifikatoren für die Inhaltserstellung

Mit Modifikatoren kannst du eine zusammensetzbare Funktion dekorieren oder erweitern. Mit Modifikatoren können Sie folgende Dinge tun:

  • Größe, Layout, Verhalten und Darstellung der zusammensetzbaren Funktion ändern
  • Informationen wie Bedienungshilfen-Labels hinzufügen
  • Nutzereingabe verarbeiten
  • Interaktionen auf hoher Ebene hinzufügen, z. B. Elemente anklickbar, scrollbar, ziehbar oder zoombar

Modifikatoren sind standardmäßige Kotlin-Objekte. Um einen Modifikator zu erstellen, rufen Sie eine der Modifier-Klassenfunktionen auf:

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

Zwei Textzeilen vor einem farbigen Hintergrund mit Abstand zum Text.

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

@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 nimmt nun die volle Breite des Geräts ein.

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

  • 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.

Es empfiehlt sich, für alle zusammensetzbaren Funktionen einen modifier-Parameter zu akzeptieren und diesen Modifikator an das erste untergeordnete Element zu übergeben, das die UI ausgibt. Das macht Ihren Code wiederverwendbarer und macht sein Verhalten vorhersehbarer und intuitiver. Weitere Informationen finden Sie in den Richtlinien für die Compose API unter Elemente akzeptieren und berücksichtigen einen Modifizierer-Parameter.

Die Reihenfolge der Modifikatoren ist wichtig

Die Reihenfolge der Modifikatorfunktionen ist wichtig. Da jede Funktion Änderungen an dem von der vorherigen Funktion zurückgegebenen Modifier vornimmt, wirkt sich die Sequenz auf das Endergebnis aus. Hier ein Beispiel:

@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 padding-Modifikator nach dem Modifizierer clickable angewendet wurde. Wenn die Reihenfolge der Modifikatoren umgekehrt wird, 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 mit integrierten Modifikatoren, mit denen Sie eine zusammensetzbare Funktion gestalten oder erweitern können. Hier sind einige gängige Modifikatoren, mit denen Sie Ihre Layouts anpassen können.

padding und size

Bei Layouts, die in „Compose“ bereitgestellt werden, werden die untergeordneten Elemente standardmäßig umschlossen. Mit dem Modifikator size können Sie jedoch eine Größe 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 nicht den Einschränkungen des übergeordneten Layouts entspricht. Wenn die zusammensetzbare Größe unabhängig von den eingehenden Einschränkungen festgelegt werden muss, verwenden Sie den Modifizierer 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

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

Wenn ein untergeordnetes Layout die gesamte für das übergeordnete Element zulässige Höhe ausfüllen soll, fügen Sie den Modifikator fillMaxHeight hinzu. Schreiben Sie 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 ist so groß wie das übergeordnete Element.

Wenn Sie das gesamte Element um einen Innenrand versehen möchten, legen Sie einen padding-Modifikator fest.

Wenn Sie einen Abstand über einer Textreferenz hinzufügen möchten, sodass ein bestimmter Abstand vom oberen Rand des Layouts zur Grundlinie erreicht wird, 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

Um ein Layout relativ zur ursprünglichen Position zu positionieren, fügen Sie den Modifikator offset hinzu und legen Sie den Versatz in 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 eines offset zu einer zusammensetzbaren Funktion seine Messungen nicht ändert:

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

Der Text wurde auf die rechte Seite des übergeordneten Containers verschoben

Der offset-Modifikator wird horizontal gemäß der Layoutrichtung angewendet. In einem Rechts-nach-rechts-Kontext wird ein positives offset-Element das Element nach rechts und in einem Links-nach-links- Kontext nach links verschoben. Wenn Sie einen Offset ohne Berücksichtigung der Layoutrichtung festlegen müssen, verwenden Sie den absoluteOffset-Modifikator, bei dem ein positiver Offset-Wert das Element immer nach rechts verschiebt.

Der offset-Modifikator stellt zwei Überlasten bereit: offset, das die Offsets als Parameter annimmt, und offset, das ein Lambda annimmt. Ausführlichere Informationen dazu, wann Sie welche dieser Methoden verwenden sollten und wie Sie die Leistung optimieren, finden Sie im Abschnitt Leistung verfassen – Lesevorgänge so lange wie möglich zurückstellen.

Sicherheit des Umfangs in Compose

In der Funktion „Compose“ gibt es Modifikatoren, die nur verwendet werden können, wenn sie auf untergeordnete Elemente bestimmter zusammensetzbarer Funktionen angewendet werden. Beim Erstellen 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 Box zu beeinflussen, verwenden Sie den Modifizierer matchParentSize. matchParentSize ist nur in BoxScope verfügbar. Daher kann sie 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, und spart Zeit durch Ausprobieren.

Modifikatoren für den Bereich informieren das übergeordnete Element über einige Informationen, die es über das untergeordnete Element wissen sollte. Diese werden auch als übergeordnete Datenmodifikatoren bezeichnet. Ihre internen Strukturen unterscheiden sich von den Modifizierern für allgemeine Zwecke, aber in Bezug auf die Verwendung spielen diese Unterschiede keine Rolle.

matchParentSize in Box

Wie bereits erwähnt, müssen Sie den matchParentSize-Modifikator verwenden, wenn ein untergeordnetes Layout die gleiche Größe wie ein übergeordnetes Box-Element haben soll, ohne dass sich dies auf die Box-Größe auswirkt.

matchParentSize ist nur innerhalb eines Box-Bereichs verfügbar, d. h., er gilt nur für direkte untergeordnete Elemente von Box zusammensetzbaren Funktionen.

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

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

Grauer Hintergrund, der den Behälter ausfüllt

Wenn fillMaxSize anstelle von matchParentSize verwendet würde, würde das Spacer den gesamten verfügbaren Speicherplatz des übergeordneten Elements einnehmen. Dadurch wird das übergeordnete Element wiederum erweitert und füllt den gesamten verfügbaren Bereich aus.

Grauer Hintergrund als Füllung des Displays

weight in Row und Column

Wie Sie im vorherigen Abschnitt zum Thema Abstand und Größe gesehen haben, wird eine zusammensetzbare Größe standardmäßig durch den Inhalt definiert, der umgebrochen wird. Sie können eine zusammensetzbare Größe so festlegen, dass sie innerhalb des übergeordneten Elements flexibel ist. Verwenden Sie dazu den weight-Modifikator, der nur in RowScope und ColumnScope verfügbar ist.

Nehmen wir eine Row, die zwei zusammensetzbare Box-Elemente enthält. Das erste Feld wird doppelt so klein wie weight der zweiten Box, also doppelt so breit. Da Row 210.dp breit ist, ist der erste Box 140.dp und der zweite 70.dp breit:

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

Die Bildbreite 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 Modifier-Schnittstelle erstellt, die eine geordnete, unveränderliche Liste einzelner Modifier.Elements darstellt.

Jede Modifier.Element repräsentiert ein individuelles Verhalten, z. B. Layout-, Zeichen- und Grafikverhalten, alle gestenbezogenen, fokussierten und semantischen Verhaltensweisen sowie Geräteeingabeereignisse. Die Reihenfolge ist entscheidend: 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öhere Bereiche schieben. Es kann aus verschiedenen Gründen dazu beitragen, die Lesbarkeit von Code oder die Leistung Ihrer Anwendung zu verbessern:

  • Die Neuzuweisung der Modifikatoren wird nicht wiederholt, wenn zusammensetzbare Funktionen neu zusammengesetzt werden, in denen sie verwendet werden.
  • Modifikatorketten können potenziell sehr lang und komplex sein. Die Wiederverwendung derselben Instanz einer Kette kann daher die Arbeitslast entlasten, die die Compose-Laufzeit beim Vergleichen ausführen muss.
  • Diese Extraktion fördert die Codereinheit, Konsistenz und Verwaltbarkeit auf 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. Sie können einen Modifizierer lediglich speichern, da es sich um datenähnliche Objekte handelt:

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

Modifikatoren bei der Beobachtung von sich häufig ändernden Zustand extrahieren und wiederverwenden

Wenn sich die Zustände in zusammensetzbaren Funktionen wie Animationsstatus oder scrollState häufig ändern, kann es zu einer erheblichen Anzahl von Neuzusammensetzungen kommen. In diesem Fall werden die Modifikatoren 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 Modifizierers erstellen, extrahieren, wiederverwenden und 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
    )
}

Modifizierer ohne Umfang extrahieren und wiederverwenden

Modifikatoren können keinen Geltungsbereich haben oder auf eine bestimmte zusammensetzbare Funktion beschränkt sein. Modifikatoren ohne Bereich können problemlos als einfache Variablen außerhalb von zusammensetzbaren Funktionen extrahiert werden:

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

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

Dies kann besonders in Kombination mit Lazy-Layouts von Vorteil sein. In den meisten Fällen ist es sinnvoll, dass alle potenziell signifikanten Elemente 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,
            )
        }
    }
}

Bereichsspezifische Modifikatoren extrahieren und wiederverwenden

Wenn Sie Modifikatoren verwenden, die sich auf bestimmte zusammensetzbare Funktionen beziehen, 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 bereichsspezifischen Modifikatoren nur an direkte untergeordnete Elemente mit demselben Bereich übergeben. Weitere Informationen dazu, warum das wichtig ist, finden Sie im Abschnitt Sicherheit des Bereichs in Composer:

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

Beachte aber, dass die Reihenfolge der Modifikatoren wichtig ist.

Weitere Informationen

Wir stellen eine vollständige Liste der Modifikatoren mit ihren Parametern und Bereichen zur Verfügung.

Weitere Übungen zum Verwenden von Modifikatoren finden Sie im Codelab zu grundlegenden Layouts im Codelab oder im Now in Android-Repository.

Weitere Informationen zu benutzerdefinierten Modifikatoren und zu ihrer Erstellung finden Sie in der Dokumentation zu Benutzerdefinierte Layouts: Verwendung des Layout-Modifikators.