Systeminterne Messungen in Compose-Layouts

Eine der Regeln von „Compose“ besagt, dass Sie Ihre untergeordneten Elemente nur einmal erfassen sollten. bei zweimaligen Messungen einer Laufzeitausnahme. Es gibt jedoch Zeiten, wenn Sie Informationen über Ihre Kinder benötigen, bevor Sie sie messen.

Mit Intrinsics können Sie untergeordnete Elemente abfragen, bevor sie tatsächlich gemessen werden.

Bei einer zusammensetzbaren Funktion können Sie nach intrinsicWidth oder intrinsicHeight fragen:

  • (min|max)IntrinsicWidth: Was ist aufgrund dieser Breite der minimale/maximale mit der Sie Ihre Inhalte richtig darstellen können?
  • (min|max)IntrinsicHeight: Was ist aufgrund dieser Höhe der minimale/maximale Wert wie groß Sie Ihre Inhalte richtig darstellen können?

Wenn Sie beispielsweise die minIntrinsicHeight einer Text mit unendlicher height wird das height von Text zurückgegeben, als ob der Text in einem in einer Zeile.

Intrinsik in Aktion

Stellen Sie sich vor, wir möchten eine zusammensetzbare Funktion erstellen, die zwei Texte auf der Bildschirm durch eine Trennlinie getrennt:

Zwei Textelemente nebeneinander, zwischen ihnen eine vertikale Trennlinie

Wie können wir das tun? Wir können ein Row mit zwei Text-Elementen haben, das wie folgt erweitert wird: und ein Divider in der Mitte. Divider soll so aussehen: hoch als die höchste Text und dünn (width = 1.dp).

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        Divider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

In der Vorschau sehen wir, dass Divider den gesamten Bildschirm einnimmt und wollen wir das nicht:

Zwei Textelemente nebeneinander, mit einer Trennlinie dazwischen, die sich jedoch unterhalb des Textes nach unten erstreckt

Das liegt daran, dass Row jedes Kind einzeln misst und die Größe Text kann nicht zur Einschränkung von Divider verwendet werden. Wir möchten, dass die Divider den verfügbaren Platz mit einer bestimmten Höhe. Dafür können wir den height(IntrinsicSize.Min)-Modifikator

height(IntrinsicSize.Min) legt fest, dass seine untergeordneten Elemente so groß sind wie ihre minimale intrinsische Höhe haben. Da sie rekursiv ist, fragt sie Row ab Kinder minIntrinsicHeight.

Wenn wir dies auf unseren Code anwenden, funktioniert es wie erwartet:

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier.height(IntrinsicSize.Min)) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        Divider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

// @Preview
@Composable
fun TwoTextsPreview() {
    MaterialTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

Mit Vorschau:

Zwei Textelemente nebeneinander, zwischen ihnen eine vertikale Trennlinie

Die minIntrinsicHeight der zusammensetzbaren Funktion „Row“ ist das Maximum minIntrinsicHeight seiner untergeordneten Elemente. Der Wert des Divider-Elements minIntrinsicHeight ist 0, da es keinen Platz belegt, wenn keine Einschränkungen vorhanden sind gegeben; der Text minIntrinsicHeight ist der des Textes für eine bestimmte width. Daher ist die height-Beschränkung des Row-Elements die maximale minIntrinsicHeight von Text. Divider erweitert seine height dann um Die durch Row vorgegebene Einschränkung height.

Intrinsische Elemente in Ihren benutzerdefinierten Layouts

Wenn Sie einen benutzerdefinierten Layout- oder layout-Modifikator erstellen, sind intrinsische Messwerte werden anhand von Schätzungen automatisch berechnet. Daher entspricht der Parameter Berechnungen möglicherweise nicht für alle Layouts korrekt sind. Diese APIs bieten Optionen um diese Standardeinstellungen zu überschreiben.

So legst du die intrinsischen Messwerte deiner benutzerdefinierten Layout fest: minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth und maxIntrinsicHeight der MeasurePolicy Schnittstelle beim Erstellen.

@Composable
fun MyCustomComposable(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        content = content,
        modifier = modifier,
        measurePolicy = object : MeasurePolicy {
            override fun MeasureScope.measure(
                measurables: List<Measurable>,
                constraints: Constraints
            ): MeasureResult {
                // Measure and layout here
                // ...
            }

            override fun IntrinsicMeasureScope.minIntrinsicWidth(
                measurables: List<IntrinsicMeasurable>,
                height: Int
            ): Int {
                // Logic here
                // ...
            }

            // Other intrinsics related methods have a default value,
            // you can override only the methods that you need.
        }
    )
}

Beim Erstellen des benutzerdefinierten layout-Modifikator die zugehörigen Methoden überschreiben in der LayoutModifier-Oberfläche.

fun Modifier.myCustomModifier(/* ... */) = this then object : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // Measure and layout here
        // ...
    }

    override fun IntrinsicMeasureScope.minIntrinsicWidth(
        measurable: IntrinsicMeasurable,
        height: Int
    ): Int {
        // Logic here
        // ...
    }

    // Other intrinsics related methods have a default value,
    // you can override only the methods that you need.
}