Einschränkungen und Modifikatorreihenfolge

In Compose können Sie mehrere Modifikatoren miteinander verketten, um das Design einer zusammensetzbaren Funktion zu ändern. Diese Modifikatorketten können sich auf die Einschränkungen auswirken, die an Composables übergeben werden und die Breiten- und Höhenbegrenzungen definieren.

Auf dieser Seite wird beschrieben, wie sich verkettete Modifikatoren auf Einschränkungen und damit auf die Messung und Platzierung von Composables auswirken.

Modifikatoren im UI-Baum

Um zu verstehen, wie sich Modifikatoren gegenseitig beeinflussen, ist es hilfreich, sich vorzustellen, wie sie in der UI-Struktur angezeigt werden, die während der Kompositionsphase generiert wird. Weitere Informationen finden Sie im Abschnitt Zusammensetzung.

In der UI-Struktur können Sie Modifikatoren als Wrapper-Knoten für die Layoutknoten visualisieren:

Code für Composables und Modifikatoren und ihre visuelle Darstellung als UI-Baum.
Abbildung 1. Modifikatoren, die Layoutknoten im UI-Baum umschließen.

Wenn Sie einer zusammensetzbaren Funktion mehr als einen Modifikator hinzufügen, wird eine Kette von Modifikatoren erstellt. Wenn Sie mehrere Modifikatoren verketten, umschließt jeder Modifikatorknoten den Rest der Kette und den Layoutknoten darin. Wenn Sie beispielsweise einen clip- und einen size-Modifikator verketten, umschließt der clip-Modifikatorknoten den size-Modifikatorknoten, der wiederum den Image-Layoutknoten umschließt.

In der Layoutphase bleibt der Algorithmus, der den Baum durchläuft, gleich, aber jeder Modifikatorknoten wird ebenfalls besucht. So kann ein Modifikator die Größenanforderungen und die Platzierung des Modifikators oder Layoutknotens ändern, den er umschließt.

Wie in Abbildung 2 dargestellt, besteht die Implementierung der Composables Image und Text aus einer Kette von Modifikatoren, die einen einzelnen Layoutknoten umschließen.

Die Implementierungen von Row und Column sind Layoutknoten, die beschreiben, wie ihre untergeordneten Elemente angeordnet werden sollen.

Die Baumstruktur von vorhin, aber jetzt ist jeder Knoten nur ein einfaches Layout, um das viele Modifikatoren gewickelt sind.
Abbildung 2: Dieselbe Baumstruktur wie in Abbildung 1, aber mit Composables im UI-Baum, die als Ketten von Modifikatoren visualisiert werden.

Zusammenfassung:

  • Modifikatoren umschließen einen einzelnen Modifikator- oder Layoutknoten.
  • Layoutknoten können mehrere untergeordnete Knoten enthalten.

In den folgenden Abschnitten wird beschrieben, wie Sie dieses mentale Modell verwenden, um über die Verkettung von Modifikatoren nachzudenken und wie sich diese auf die Größe von Composables auswirkt.

Einschränkungen in der Layoutphase

Die Layoutphase folgt einem dreistufigen Algorithmus, um die Breite, Höhe und x, y-Koordinate jedes Layoutknotens zu ermitteln:

  1. Untergeordnete Elemente messen: Ein Knoten misst seine untergeordneten Elemente, falls vorhanden.
  2. Eigene Größe festlegen: Basierend auf diesen Messungen entscheidet ein Knoten über seine eigene Größe.
  3. Untergeordnete Elemente platzieren: Jedes untergeordnete Element wird relativ zur Position des Knotens platziert.

Constraints hilft dabei, in den ersten beiden Schritten des Algorithmus die richtigen Größen für die Knoten zu finden. Einschränkungen definieren die Mindest- und Höchstgrenzen für die Breite und Höhe eines Knotens. Wenn der Knoten seine Größe festlegt, sollte die gemessene Größe innerhalb dieses Größenbereichs liegen.

Einschränkungstypen

Eine Einschränkung kann eine der folgenden sein:

  • Begrenzt: Der Knoten hat eine maximale und minimale Breite und Höhe.
Eingeschränkte Einschränkungen unterschiedlicher Größe innerhalb eines Containers.
Abbildung 3. Begrenzte Einschränkungen.
  • Unbegrenzt: Der Knoten ist nicht auf eine bestimmte Größe beschränkt. Die maximale Breite und Höhe sind auf unendlich festgelegt.
Unbegrenzte Einschränkungen, bei denen Breite und Höhe auf unendlich festgelegt sind. Die Einschränkungen gehen über den Container hinaus.
Abbildung 4. Unbegrenzte Einschränkungen.
  • Genau: Der Knoten muss eine genaue Größenanforderung erfüllen. Die Mindest- und Höchstgrenzen sind auf denselben Wert festgelegt.
Genaue Einschränkungen, die einer genauen Größenanforderung innerhalb des Containers entsprechen.
Abbildung 5. Genaue Einschränkungen.
  • Kombination: Der Knoten folgt einer Kombination der vorherigen Einschränkungstypen. Eine Einschränkung könnte beispielsweise die Breite begrenzen, aber eine unbegrenzte maximale Höhe zulassen, oder eine genaue Breite festlegen, aber eine begrenzte Höhe vorgeben.
Zwei Container, die Kombinationen aus begrenzten und unbegrenzten Einschränkungen sowie genauen Breiten und Höhen zeigen.
Abbildung 6. Kombinationen aus begrenzten und unbegrenzten Einschränkungen sowie genauen Breiten und Höhen.

Im nächsten Abschnitt wird beschrieben, wie diese Einschränkungen von einem übergeordneten Element an ein untergeordnetes Element übergeben werden.

So werden Einschränkungen vom übergeordneten an das untergeordnete Element weitergegeben

Im ersten Schritt des Algorithmus, der unter Einschränkungen in der Layoutphase beschrieben wird, werden Einschränkungen im UI-Baum von übergeordneten an untergeordnete Elemente weitergegeben.

Wenn ein übergeordneter Knoten seine untergeordneten Knoten misst, stellt er jedem untergeordneten Knoten diese Einschränkungen zur Verfügung, damit sie wissen, wie groß oder klein sie sein dürfen. Wenn es dann seine eigene Größe festlegt, hält es sich auch an die Einschränkungen, die von seinen eigenen übergeordneten Elementen übergeben wurden.

Auf übergeordneter Ebene funktioniert der Algorithmus so:

  1. Um die Größe zu bestimmen, die er tatsächlich einnehmen möchte, misst der Stammknoten im UI-Baum seine untergeordneten Elemente und leitet dieselben Einschränkungen an sein erstes untergeordnetes Element weiter.
  2. Wenn das untergeordnete Element ein Modifikator ist, der sich nicht auf die Messung auswirkt, werden die Einschränkungen an den nächsten Modifikator weitergeleitet. Die Einschränkungen werden unverändert in der Modifikatorkette weitergegeben, bis ein Modifikator erreicht wird, der sich auf die Messung auswirkt. Die Einschränkungen werden dann entsprechend angepasst.
  3. Sobald ein Knoten erreicht wird, der keine untergeordneten Elemente hat (ein sogenannter „Blattknoten“), wird seine Größe anhand der übergebenen Einschränkungen bestimmt und diese aufgelöste Größe an das übergeordnete Element zurückgegeben.
  4. Das übergeordnete Element passt seine Einschränkungen basierend auf den Messungen dieses untergeordneten Elements an und ruft das nächste untergeordnete Element mit diesen angepassten Einschränkungen auf.
  5. Sobald alle untergeordneten Elemente eines übergeordneten Elements gemessen wurden, entscheidet der übergeordnete Knoten über seine eigene Größe und teilt diese seinem eigenen übergeordneten Element mit.
  6. Auf diese Weise wird der gesamte Baum nach dem Tiefensuchverfahren durchlaufen. Schließlich haben alle Knoten ihre Größe festgelegt und der Messschritt ist abgeschlossen.

Ein ausführliches Beispiel findest du im Video Constraints and modifier order.

Modifikatoren, die sich auf Einschränkungen auswirken

Im vorherigen Abschnitt haben Sie gelernt, dass sich einige Modifikatoren auf die Größe von Einschränkungen auswirken können. In den folgenden Abschnitten werden bestimmte Modifikatoren beschrieben, die sich auf Einschränkungen auswirken.

Der size-Modifikator

Der Modifikator size deklariert die bevorzugte Größe des Inhalts.

Der folgende UI-Baum sollte beispielsweise in einem Container mit 300dp von 200dp gerendert werden. Die Einschränkungen sind begrenzt und erlauben Breiten zwischen 100dp und 300dp sowie Höhen zwischen 100dp und 200dp:

Ein Teil eines UI-Baums, bei dem der Größenmodifikator einen Layoutknoten umschließt, und die Darstellung der durch den Größenmodifikator in einem Container festgelegten Einschränkungen.
Abbildung 7. Begrenzte Einschränkungen im UI-Baum und ihre Darstellung in einem Container.

Der Modifikator size passt eingehende Einschränkungen an den übergebenen Wert an. In diesem Beispiel ist der Wert 150dp:

Wie in Abbildung 7, nur dass der Größenmodifikator die eingehenden Einschränkungen an den übergebenen Wert anpasst.
Abbildung 8. Der size-Modifikator, der Einschränkungen auf 150dp anpasst.

Wenn die Breite und Höhe kleiner als die kleinste Einschränkungsgrenze oder größer als die größte Einschränkungsgrenze sind, gleicht der Modifikator die übergebenen Einschränkungen so genau wie möglich ab, hält sich aber an die übergebenen Einschränkungen:

Zwei UI-Bäume und ihre entsprechenden Darstellungen in Containern. Im ersten Fall akzeptiert der Größenmodifikator die eingehenden Einschränkungen. Im zweiten Fall passt sich der Größenmodifikator so weit wie möglich an die zu großen Einschränkungen an, sodass die Einschränkungen den Container füllen.
Abbildung 9. Der size-Modifikator, der die übergebene Einschränkung so genau wie möglich einhält.

Beachten Sie, dass das Verketten mehrerer size-Modifikatoren nicht funktioniert. Der erste size-Modifikator legt sowohl die Mindest- als auch die Höchstbeschränkung auf einen festen Wert fest. Auch wenn der zweite Größenmodifikator eine kleinere oder größere Größe anfordert, muss er sich an die genauen Grenzen halten, die übergeben wurden. Diese Werte werden also nicht überschrieben:

Eine Kette von zwei Größenmodifikatoren in der UI-Struktur und ihre Darstellung in einem Container, die das Ergebnis des ersten übergebenen Werts und nicht des zweiten Werts ist.
Abbildung 10. Eine Kette von zwei size-Modifikatoren, bei der der zweite übergebene Wert (50dp) den ersten Wert (100dp) nicht überschreibt.

Der requiredSize-Modifikator

Verwenden Sie den Modifikator requiredSize anstelle von size, wenn der Knoten die eingehenden Einschränkungen überschreiben soll. Der Modifikator requiredSize ersetzt die eingehenden Einschränkungen und übergibt die von Ihnen angegebene Größe als genaue Grenzen.

Wenn die Größe im Baum nach oben weitergegeben wird, wird der untergeordnete Knoten im verfügbaren Bereich zentriert:

Die Modifikatoren „size“ und „requiredSize“ in einem UI-Baum und die entsprechende Darstellung in einem Container. Die Einschränkungen des Modifikators „requiredSize“ überschreiben die Einschränkungen des Modifikators „size“.
Abbildung 11. Der requiredSize-Modifikator überschreibt eingehende Einschränkungen des size-Modifikators.

Die Modifikatoren width und height

Der Modifikator size passt sowohl die Breite als auch die Höhe der Einschränkungen an. Mit dem Modifikator width können Sie eine feste Breite festlegen, die Höhe jedoch unbestimmt lassen. Mit dem Modifikator height können Sie eine feste Höhe festlegen, die Breite jedoch unbestimmt lassen:

Zwei UI-Bäume, einer mit dem Breitenmodifikator und seiner Containerdarstellung und der andere mit dem Höhenmodifikator und seiner Darstellung.
Abbildung 12. Der Modifikator width und der Modifikator height legen eine feste Breite bzw. Höhe fest.

Der sizeIn-Modifikator

Mit dem Modifikator sizeIn können Sie genaue Mindest- und Höchstwerte für Breite und Höhe festlegen. Verwenden Sie den Modifikator sizeIn, wenn Sie die Einschränkungen genau steuern möchten.

Ein UI-Baum mit dem sizeIn-Modifikator, bei dem die minimale und maximale Breite und Höhe festgelegt sind, und seine Darstellung in einem Container.
Abbildung 13. Der Modifikator sizeIn mit den festgelegten Werten minWidth, maxWidth, minHeight und maxHeight.

Beispiele

In diesem Abschnitt wird die Ausgabe mehrerer Code-Snippets mit verketteten Modifikatoren gezeigt und erläutert.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

Dieses Snippet liefert folgende Ausgabe:

Ein blaues Quadrat, das den übergeordneten Container ausfüllt.
Abbildung 14. Image füllt die maximale Größe als Ergebnis der Modifikatorkette aus.
  • Der Modifikator fillMaxSize ändert die Einschränkungen, sodass sowohl die minimale Breite als auch die minimale Höhe auf den maximalen Wert festgelegt werden – 300dp für die Breite und 200dp für die Höhe.
  • Obwohl der Modifikator size eine Größe von 50dp verwenden möchte, muss er sich an die eingehenden Mindesteinschränkungen halten. Der Modifikator size gibt also auch die genauen Einschränkungsgrenzen von 300 mal 200 aus und ignoriert den im Modifikator size angegebenen Wert.
  • Das Image folgt diesen Grenzen und meldet eine Größe von 300 x 200, die im Baum nach oben weitergegeben wird.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

Dieses Snippet liefert folgende Ausgabe:

Ein kleines blaues Quadrat, das sich in der Mitte des übergeordneten Containers befindet.
Abbildung 15. Image ist zentriert und auf 50dp festgelegt.
  • Der Modifikator fillMaxSize passt die Einschränkungen so an, dass sowohl die Mindestbreite als auch die Mindesthöhe auf den Höchstwert festgelegt werden – 300dp für die Breite und 200dp für die Höhe.
  • Mit dem Modifikator wrapContentSize werden die Mindesteinschränkungen zurückgesetzt. Während fillMaxSize also zu festen Einschränkungen geführt hat, wird es durch wrapContentSize wieder auf begrenzte Einschränkungen zurückgesetzt. Der folgende Knoten kann jetzt wieder den gesamten Platz einnehmen oder kleiner als der gesamte Platz sein.
  • Der Modifikator size legt die Einschränkungen auf die Mindest- und Höchstgrenzen von 50 fest.
  • Der Image wird in eine Größe von 50 × 50 aufgelöst und der size-Modifikator leitet dies weiter.
  • Der Modifikator wrapContentSize hat ein spezielles Attribut. Es nimmt sein untergeordnetes Element und platziert es in der Mitte der verfügbaren Mindestgrenzen, die an es übergeben wurden. Die Größe, die es an seine übergeordneten Elemente weitergibt, entspricht also den Mindestgrenzen, die an es übergeben wurden.

Wenn Sie nur drei Modifikatoren kombinieren, können Sie eine Größe für das Composable festlegen und es im übergeordneten Element zentrieren.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

Dieses Snippet liefert folgende Ausgabe:

Eine kreisförmige Form, die aufgrund der Reihenfolge der Modifikatoren falsch zugeschnitten ist.
Abbildung 16. Eine falsch zugeschnittene Form aufgrund der Reihenfolge der Modifikatoren.
  • Der Modifikator clip ändert die Einschränkungen nicht.
  • Der Modifikator padding senkt die maximalen Einschränkungen.
  • Der Modifikator size setzt alle Einschränkungen auf 100dp.
  • Das Image hält sich an diese Einschränkungen und meldet eine Größe von 100dp x 100dp.
  • Der Modifikator padding fügt der von Image gemeldeten Größe auf allen Seiten 10dp hinzu. Das Layout mit Padding meldet also eine Breite und Höhe von 120dp.
  • In der Zeichenphase wirkt sich der clip-Modifikator auf eine Canvas von 120dp x 120dp aus. Es wird eine kreisförmige Maske dieser Größe erstellt.
  • Der Modifikator padding rückt dann den Inhalt auf allen Seiten um 10dp ein, wodurch die Canvas-Größe für Image auf 100dp mal 100dp reduziert wird.
  • Das Image wird in diesem kleineren Canvas gezeichnet. Das Bild wird basierend auf dem ursprünglichen Kreis von 120dp zugeschnitten, sodass das Ergebnis nicht rund ist.