Pomiary wewnętrzne w układach tworzenia wiadomości

Jedną z reguł tworzenia wiadomości jest, aby dzieci zliczały się tylko raz; dwa razy powoduje wystąpienie wyjątku w czasie działania. Jednak czasami przed dokonaniem pomiarów potrzebujesz informacji o swoich dzieciach.

Intrinsics umożliwia wysyłanie zapytań do dzieci, zanim zostaną one zmierzone.

Do funkcji kompozycyjnej możesz poprosić o intrinsicWidth lub intrinsicHeight:

  • (min|max)IntrinsicWidth: Jaka jest minimalna/maksymalna szerokość przy tej wysokości, jaką można pomalować treści?
  • (min|max)IntrinsicHeight: Przy tej szerokości jaka jest minimalna/maksymalna wysokość, na jaką możesz prawidłowo pomalować treści?

Jeśli na przykład zapytasz o minIntrinsicHeight elementu Text z nieskończonym width, otrzymasz height z metody Text, tak jakby tekst był narysowany w jednej linii.

Cechy wewnętrzne w praktyce

Wyobraź sobie, że chcemy utworzyć kompozycję, która wyświetla na ekranie 2 teksty oddzielone separatorem w ten sposób:

Dwa elementy tekstowe obok siebie, z pionowym separatorem między nimi

Jak to zrobić? Możemy mieć obiekt Row z 2 elementami Text w środku, który się rozwija oraz Divider w środku. Chcemy, aby element Divider miał taką samą wysokość, jak największy element Text i cienki (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
        )
    }
}

Gdy wyświetlimy podgląd, zobaczysz, że Divider rozwija się na cały ekran, a to nie jest potrzebne:

2 elementy tekstowe umieszczone obok siebie i rozdzielane separatorem, który rozciąga się poniżej dolnej części tekstu

Dzieje się tak, ponieważ Row mierzy każdy element podrzędny oddzielnie, a wysokości Text nie można wykorzystać do ograniczenia elementu Divider. Divider ma wypełniać dostępną przestrzeń o danej wysokości. Do tego możemy użyć modyfikatora height(IntrinsicSize.Min) .

W height(IntrinsicSize.Min) rozmiar dziecka musi być taki sam jak jego minimalna wysokość. Jest rekurencyjny, więc wysyła zapytanie Row i jego elementy podrzędne minIntrinsicHeight.

Jeśli zastosujesz go do naszego kodu, będzie to działać zgodnie z oczekiwaniami:

@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")
        }
    }
}

W przypadku podglądu:

Dwa elementy tekstowe obok siebie, z pionowym separatorem między nimi

minIntrinsicHeight elementu kompozycyjnego Row będzie stanowić maksymalną liczbę minIntrinsicHeight elementów podrzędnych. Wartość minIntrinsicHeight elementu Divider ma wartość 0, ponieważ nie zajmuje ona miejsca, jeśli nie ma żadnych ograniczeń. Pole „Text minIntrinsicHeight” będzie zawierać tekst z określonym parametrem width. Dlatego ograniczenie height elementu Row będzie wartością maksymalną minIntrinsicHeight z Text s. Divider rozwinie się wtedy height do ograniczenia height podanego przez Row.

Elementy wewnętrzne w układach niestandardowych

Przy tworzeniu niestandardowego modyfikatora Layout lub layout pomiary wewnętrzne są obliczane automatycznie na podstawie przybliżeń. Dlatego też obliczenia mogą nie być poprawne w przypadku niektórych układów. Te interfejsy API oferują opcje zastępowania tych wartości domyślnych.

Aby określić własne pomiary niestandardowego interfejsu Layout, zastąp minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth i maxIntrinsicHeight interfejsu MeasurePolicy podczas jego tworzenia.

@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.
        }
    )
}

Podczas tworzenia niestandardowego modyfikatora layout zastąp powiązane metody w interfejsie LayoutModifier.

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