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

Jedna z zasad Compose mówi, że dzieci należy mierzyć tylko raz. Dwukrotne zmierzenie dzieci powoduje wyjątek w czasie działania. Czasami jednak przed zmierzeniem dzieci potrzebujesz pewnych informacji na ich temat.

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

W przypadku funkcji kompozycyjnej możesz poprosić o jej IntrinsicSize.Min lub IntrinsicSize.Max:

  • Modifier.width(IntrinsicSize.Min) – jaka jest minimalna szerokość, która umożliwia prawidłowe wyświetlanie treści?
  • Modifier.width(IntrinsicSize.Max) – Jaka jest maksymalna szerokość, której potrzebujesz, aby prawidłowo wyświetlać treści?
  • Modifier.height(IntrinsicSize.Min) – Jaka jest minimalna wysokość potrzebna do prawidłowego wyświetlania treści?
  • Modifier.height(IntrinsicSize.Max) – Jaka jest maksymalna wysokość, której potrzebujesz, aby prawidłowo wyświetlać treści?

Jeśli na przykład w układzie niestandardowym zapytasz o minIntrinsicHeight elementu Text z ograniczeniami width, zwróci on height elementu Text z tekstem narysowanym w jednym wierszu.

Funkcje pierwotne w praktyce

Możesz utworzyć komponent, który wyświetla na ekranie 2 teksty oddzielone separatorem:

Dwa elementy tekstowe obok siebie, oddzielone pionową linią

Aby to zrobić, użyj elementu Row z 2 komponentami Text, które wypełniają dostępną przestrzeń, oraz elementu Divider pośrodku. Element Divider powinien być tak wysoki jak najwyższy 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
        )
        VerticalDivider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

Element Divider rozszerza się na cały ekran, co nie jest pożądanym zachowaniem:

Dwa elementy tekstowe obok siebie, oddzielone separatorem, który sięga poniżej dolnej krawędzi tekstu.

Dzieje się tak, ponieważ Row mierzy każde dziecko osobno, a wysokość Text nie może być używana do ograniczania Divider.

Aby element Divider wypełniał dostępne miejsce o określonej wysokości, użyj modyfikatora height(IntrinsicSize.Min).

height(IntrinsicSize.Min) dostosowuje wysokość elementów podrzędnych do ich minimalnej wysokości wewnętrznej. Ten modyfikator jest rekurencyjny, więc wysyła zapytanie o minIntrinsicHeight Row i jego elementów podrzędnych.

Zastosowanie tego modyfikatora do kodu sprawi, że będzie on 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
        )
        VerticalDivider(
            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")
        }
    }
}

Z podglądem:

Dwa elementy tekstowe obok siebie, oddzielone pionową linią

Wysokość elementu Row jest określana w ten sposób:

  • Wartość minIntrinsicHeight elementu kompozycyjnego Row jest maksymalną wartością minIntrinsicHeight jego elementów podrzędnych.
  • Wartość minIntrinsicHeight elementu Divider wynosi 0, ponieważ nie zajmuje on miejsca, jeśli nie podano żadnych ograniczeń.
  • Wartość Text minIntrinsicHeight to wartość tekstu dla konkretnego elementu width.
  • Dlatego ograniczenie height elementu Row staje się maksymalną wartością minIntrinsicHeight elementów Text.
  • Wtedy Divider rozszerza swój height do ograniczenia height podanego przez Row.

Wartości wewnętrzne w układach niestandardowych

Podczas tworzenia niestandardowego modyfikatora Layout lub layout pomiary wewnętrzne są obliczane automatycznie na podstawie przybliżeń. Dlatego obliczenia mogą nie być prawidłowe w przypadku wszystkich układów. Te interfejsy API oferują opcje zastępowania tych ustawień domyślnych.

Aby określić pomiary wewnętrzne niestandardowego Layout, podczas jego tworzenia zastąp wartości minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidthmaxIntrinsicHeight interfejsu MeasurePolicy.

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