Einschränkungen und Modifikatorreihenfolge

In Compose können Sie mehrere Modifier verketten, um das Erscheinungsbild eines Composables zu ändern. Diese Modifiziererketten 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 Modifizierer 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 anzusehen, wie sie im UI-Baum dargestellt werden, der während der Kompositionsphase generiert wird. Weitere Informationen finden Sie im Abschnitt Zusammensetzung.

Im UI-Baum können Sie Modifikatoren als Wrapper-Knoten für die Layoutknoten visualisieren:

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

Wenn Sie einem Composable-Element mehrere Modifikatoren hinzufügen, entsteht eine Kette von Modifikatoren. Wenn Sie mehrere Modifikatoren verketten, umschließt jeder Modifikator-Knoten den Rest der Kette und den Layoutknoten. Wenn Sie beispielsweise einen clip- und einen size-Modifikator verketten, umschließt der clip-Modifikator-Knoten den size-Modifikator-Knoten, der wiederum den Image-Layoutknoten umschließt.

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

Wie in Abbildung 2 zu sehen ist, besteht die Implementierung der Composables Image und Text selbst aus einer Kette von Modifikatoren, die einen einzelnen Layoutknoten umschließen. Die Implementierungen von Row und Column sind einfach Layoutknoten, die beschreiben, wie ihre untergeordneten Elemente angeordnet werden sollen.

Die Baumstruktur von zuvor, aber jetzt ist jeder Knoten nur ein einfaches Layout mit vielen Modifikator-Wrapping-Knoten.
Abbildung 2. Dieselbe Baumstruktur wie in Abbildung 1, aber mit Composables im UI-Baum, die als Modifier-Ketten visualisiert werden.

Zusammenfassend lässt sich sagen:

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

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

Einschränkungen in der Layoutphase

In der Layoutphase wird ein dreistufiger Algorithmus verwendet, um die Breite, Höhe und x- und y-Koordinaten jedes Layoutknotens zu ermitteln:

  1. Kinder messen: Ein Knoten misst seine untergeordneten Elemente, sofern vorhanden.
  2. Größe selbst festlegen: Anhand dieser Messungen legt ein Knoten seine eigene Größe fest.
  3. Untergeordnete Elemente platzieren: Jeder untergeordnete Knoten wird relativ zur Position eines Knotens platziert.

Constraints helfen dabei, in den ersten beiden Schritten des Algorithmus die richtigen Größen für die Knoten zu finden. Mit Einschränkungen werden die minimalen und maximalen Grenzen für die Breite und Höhe eines Knotens definiert. Wenn der Knoten seine Größe bestimmt, sollte seine gemessene Größe in diesen Größenbereich fallen.

Arten von Einschränkungen

Eine Einschränkung kann Folgendes sein:

  • Begrenzt: Der Knoten hat eine maximale und eine minimale Breite und Höhe.
Begrenzte Constraints unterschiedlicher Größe in einem Container.
Abbildung 3. Begrenzte Einschränkungen.
  • Unbegrenzt: Die Größe des Knotens ist nicht begrenzt. Die maximalen Breiten- und Höhenbegrenzungen sind auf unendlich festgelegt.
Unbegrenzte Constraints, bei denen Breite und Höhe auf unendlich festgelegt sind. Die Einschränkungen gehen über den Container hinaus.
Abbildung 4. Unbounded constraints.
  • Genau: Der Knoten wird aufgefordert, eine genaue Größenanforderung zu 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: Für den Knoten gilt eine Kombination der oben genannten Einschränkungstypen. Eine Einschränkung könnte beispielsweise die Breite begrenzen, während eine unbegrenzte maximale Höhe zulässig ist, oder eine genaue Breite festlegen, aber eine begrenzte Höhe vorsehen.
Zwei Container mit Kombinationen aus begrenzten und unbegrenzten Einschränkungen sowie genauen Breiten und Höhen.
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 an ein untergeordnetes Element übergeben werden.

So werden Einschränkungen von übergeordneten an untergeordnete Elemente weitergegeben

Im ersten Schritt des im Abschnitt Einschränkungen in der Layoutphase beschriebenen Algorithmus werden Einschränkungen im UI-Baum von übergeordneten zu untergeordneten Elementen weitergegeben.

Wenn ein übergeordneter Knoten seine untergeordneten Knoten rendert, gibt er diese Einschränkungen an jeden untergeordneten Knoten weiter, um ihm mitzuteilen, wie groß oder klein er sein darf. Wenn es dann seine eigene Größe festlegt, hält es sich auch an die Einschränkungen, die von seinen eigenen Eltern übergeben wurden.

Auf übergeordneter Ebene funktioniert der Algorithmus so:

  1. Um die Größe zu bestimmen, die sie tatsächlich einnehmen soll, 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 Modifikatorfolge weitergegeben, bis ein Modifikator erreicht wird, der sich auf die Analyse 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 festgelegt und 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, bestimmt das übergeordnete Element seine eigene Größe und teilt sie seinem eigenen übergeordneten Element mit.
  6. Auf diese Weise wird der gesamte Baum in der Tiefe durchlaufen. Schließlich haben alle Knoten ihre Größen festgelegt und der Messschritt ist abgeschlossen.

Ein ausführliches Beispiel finden Sie im Video Constraints and modifier order.

Modifikatoren, die sich auf Einschränkungen auswirken

Im vorherigen Abschnitt haben Sie erfahren, 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.

size-Modifikator

Mit dem Modifikator size wird die bevorzugte Größe des Inhalts deklariert.

Der folgende UI-Baum sollte beispielsweise in einem Container mit 300dp × 200dp gerendert werden. Die Einschränkungen sind begrenzt. Die Breite darf zwischen 100dp und 300dp liegen, die Höhe zwischen 100dp und 200dp:

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

Mit dem Modifikator size werden eingehende Einschränkungen so angepasst, dass sie dem übergebenen Wert entsprechen. In diesem Beispiel ist der Wert 150dp:

Wie Abbildung 7, nur dass der Größenmodifikator eingehende Einschränkungen an den übergebenen Wert anpasst.
Abbildung 8. Der size-Modifikator, mit dem Einschränkungen an 150dp angepasst werden.

Wenn die Breite und Höhe kleiner als die kleinste oder größer als die größte Einschränkungsgrenze sind, entspricht der Modifikator den übergebenen Einschränkungen so genau wie möglich, während er die übergebenen Einschränkungen einhält:

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

Das Verketten mehrerer size-Modifizierer funktioniert nicht. Mit dem ersten size-Modifikator werden sowohl die Mindest- als auch die Höchstwerte auf einen festen Wert festgelegt. Auch wenn mit dem zweiten Größenmodifikator eine kleinere oder größere Größe angefordert wird, muss er sich an die übergebenen genauen Grenzen halten. Er überschreibt diese Werte also nicht:

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

requiredSize-Modifikator

Verwenden Sie den Modifikator requiredSize anstelle von size, wenn Ihr Knoten die eingehenden Einschränkungen überschreiben soll. Mit dem Modifikator requiredSize werden die eingehenden Einschränkungen ersetzt und die von Ihnen angegebene Größe als genaue Grenzen übergeben.

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

Der Modifier „size“ und „requiredSize“ werden in einem UI-Baum verkettet 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.

width- und height-Modifikatoren

Mit dem Modifizierer size werden sowohl die Breite als auch die Höhe der Einschränkungen angepasst. Mit dem Modifikator width können Sie eine feste Breite festlegen, die Höhe jedoch offen lassen. Mit dem Modifikator height können Sie eine feste Höhe festlegen, die Breite jedoch offen lassen:

Zwei UI-Baumstrukturen: eine mit dem Breitenmodifikator und seiner Containerdarstellung und die andere mit dem Höhenmodifikator und seiner Darstellung.
Abbildung 12. Mit dem Modifizierer width wird eine feste Breite und mit dem Modifizierer height eine feste Höhe festgelegt.

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, in dem die minimale und maximale Breite und Höhe festgelegt sind, und seine Darstellung in einem Container.
Abbildung 13. Der Modifikator sizeIn mit minWidth, maxWidth, minHeight und maxHeight ist festgelegt.

Beispiele

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

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

Dieses Snippet erzeugt die folgende Ausgabe:

  • Mit dem Modifikator fillMaxSize werden die Einschränkungen so geändert, dass sowohl die Mindestbreite als auch die Mindesthöhe auf den Maximalwert festgelegt werden: 300dp für die Breite und 200dp für die Höhe.
  • Auch wenn mit dem Modifier size eine Größe von 50dp verwendet werden soll, müssen die eingehenden Mindestanforderungen eingehalten werden. Der Modifikator size gibt also auch die genauen Einschränkungsgrenzen von 300 nach 200 aus. Der im Modifikator size angegebene Wert wird effektiv ignoriert.
  • Das Image folgt diesen Grenzen und meldet eine Größe von 300 × 200, die bis zum Stamm des Baums weitergegeben wird.

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

Dieses Snippet erzeugt die folgende Ausgabe:

  • Mit dem Modifikator fillMaxSize werden die Einschränkungen so angepasst, dass sowohl die Mindestbreite als auch die Mindesthöhe auf den Höchstwert festgelegt werden – 300dp in der Breite und 200dp in der Höhe.
  • Mit dem Modifikator wrapContentSize werden die Mindestanforderungen zurückgesetzt. Während fillMaxSize zu festen Einschränkungen führte, wrapContentSize wird es auf begrenzte Einschränkungen zurückgesetzt. Der folgende Knoten kann jetzt wieder den gesamten Bereich einnehmen oder kleiner als der gesamte Bereich sein.
  • Mit dem Modifikator size werden die Beschränkungen auf die Mindest- und Höchstgrenzen von 50 festgelegt.
  • Image wird in eine Größe von 50 × 50 aufgelöst und der Modifikator size leitet dies weiter.
  • Der Modifikator wrapContentSize hat eine besondere Eigenschaft. Das untergeordnete Element wird in der Mitte der verfügbaren Mindestgrenzen platziert, die an das Element übergeben wurden. Die Größe, die an die übergeordneten Elemente übergeben wird, entspricht also den minimalen Grenzen, die an das Element übergeben wurden.

Durch die Kombination von nur drei Modifikatoren können Sie eine Größe für das Composable definieren und es im übergeordneten Element zentrieren.

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

Dieses Snippet erzeugt die folgende Ausgabe:

  • Der Modifikator clip ändert die Einschränkungen nicht.
    • Der Modifikator padding senkt die maximalen Einschränkungen.
    • Mit dem Modifikator size werden alle Einschränkungen auf 100dp festgelegt.
    • Das Image entspricht diesen Einschränkungen und hat eine Größe von 100 × 100dp.
    • Mit dem Modifizierer padding wird 10dp für alle Größen hinzugefügt. Die angegebene Breite und Höhe wird also um 20dp erhöht.
    • In der Zeichenphase wirkt sich der clip-Modifikator auf einen Canvas von 120 × 120dp aus. Es wird also eine kreisförmige Maske mit dieser Größe erstellt.
    • Der Modifizierer padding setzt den Inhalt dann um 10dp auf allen Größen ein, sodass die Leinwandgröße um 100dp auf 100 verringert wird.
    • Die Image wird in diesem Canvas gezeichnet. Das Bild wird basierend auf dem ursprünglichen Kreis von 120dp zugeschnitten, sodass das Ergebnis nicht rund ist.