Jedną z reguł usługi Compose jest to, że elementy potomne należy mierzyć tylko raz. Podwójne zliczanie elementów potomnych powoduje wyjątek czasu wykonywania. Czasami jednak przed pomiarem potrzebujesz pewnych informacji o swoich dzieciach.
Za pomocą funkcji Intrinsics możesz wysyłać zapytania dotyczące elementów składowych, zanim zostaną one zmierzone.
W przypadku komponentu możesz poprosić o intrinsicWidth
lub intrinsicHeight
:
(min|max)IntrinsicWidth
: Przy tej szerokości, jaka jest minimalna/maksymalna szerokość, w której można prawidłowo wyświetlić treści?(min|max)IntrinsicHeight
: Jaka jest minimalna/maksymalna wysokość, przy której można prawidłowo namalować treści?
Jeśli np. poprosisz o minIntrinsicHeight
w przypadku Text
z nieskończoną liczbą wierszy height
, zwróci on height
w przypadku Text
tak, jakby tekst był zapisany w jednym wierszu.
Funkcje pierwotne w praktyce
Załóżmy, że chcemy utworzyć komponent wyświetlający na ekranie 2 teksty rozdzielone separatorem:
Jak możemy to zrobić? Możemy mieć Row
z 2 Text
wewnątrz, które rozszerza się tak bardzo, jak to możliwe, oraz Divider
w środku. Chcemy, aby Divider
był tak wysoki jak najwyższy 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 ) HorizontalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } }
Jeśli wyświetlimy podgląd, zobaczymy, że Divider
zajmuje cały ekran, a nie chcemy tego:
Dzieje się tak, ponieważ funkcja Row
mierzy każdy element podrzędny osobno, a wysokość elementu Text
nie może być używana do ograniczania elementu Divider
. Chcemy, aby element Divider
wypełniał dostępną przestrzeń o określonej wysokości. W tym celu możemy użyć modyfikatora height(IntrinsicSize.Min)
.
height(IntrinsicSize.Min)
określa rozmiary swoich elementów, które muszą mieć wysokość odpowiadającą ich minimalnej wysokości. Jest to zapytanie rekurencyjne, które przeszuka Row
i jego podelementy minIntrinsicHeight
.
Po zastosowaniu tego w naszym kodzie 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 ) HorizontalDivider( 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:
Wartość Row
kompozytowa będzie równa maksymalnej wartości minIntrinsicHeight
jego elementów.minIntrinsicHeight
Wartość minIntrinsicHeight
elementu Divider
to 0, ponieważ nie zajmuje on miejsca, jeśli nie podano żadnych ograniczeń. Wartość Text
minIntrinsicHeight
będzie taka jak wartość tekstu w danym width
. Dlatego ograniczenie height
elementu Row
będzie maksymalną wartością minIntrinsicHeight
elementu Text
. Divider
rozszerzy wtedy swoje height
na ograniczenie height
podane przez Row
.
Elementy niestandardowe w układach niestandardowych
Podczas tworzenia niestandardowego modyfikatora Layout
lub layout
pomiary wewnętrzne są obliczane automatycznie na podstawie przybliżeń. Dlatego obliczenia mogą być nieprawidłowe w przypadku niektórych układów. Te interfejsy API oferują opcje umożliwiające zastąpienie tych ustawień domyślnych.
Aby określić wartości atrybutów wewnętrznych niestandardowego Layout
, podczas jego tworzenia zastąpij atrybuty minIntrinsicWidth
, minIntrinsicHeight
, maxIntrinsicWidth
i maxIntrinsicHeight
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ąpij 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. }
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Układy niestandardowe {:#custom-layouts }
- Linia wyrównania w Jetpack Compose
- Etapy Jetpack Compose