Одно из правил Compose заключается в том, что вам следует измерять своих детей только один раз; измерение детей дважды вызывает исключение во время выполнения. Однако бывают случаи, когда вам нужна некоторая информация о ваших детях, прежде чем измерять их.
Intrinsics позволяет опрашивать детей до того, как они будут фактически измерены.
Для компонуемого объекта вы можете запросить его intrinsicWidth
или intrinsicHeight
:
-
(min|max)IntrinsicWidth
: учитывая эту ширину, какова минимальная/максимальная ширина, на которой вы можете правильно отобразить свой контент? -
(min|max)IntrinsicHeight
: Учитывая эту высоту, какова минимальная/максимальная высота, на которой вы можете правильно отрисовать свой контент?
Например, если вы запросите minIntrinsicHeight
для Text
с бесконечной height
, он вернет height
Text
, как если бы текст был нарисован в одну строку.
Внутренности в действии
Представьте, что мы хотим создать составной объект, который отображает на экране два текста, разделенных разделителем, например:
Как мы можем это сделать? У нас может быть Row
с двумя Text
внутри, которые расширяются настолько, насколько это возможно, и Divider
посередине. Мы хотим, чтобы Divider
был таким же высоким, как самый высокий Text
, и тонким ( 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 ) } }
Если мы просмотрим это, мы увидим, что Divider
расширяется на весь экран, а это не то, что нам нужно:
Это происходит потому, что Row
измеряет каждого дочернего элемента индивидуально, а высоту Text
нельзя использовать для ограничения Divider
. Мы хотим, чтобы Divider
заполнил доступное пространство заданной высотой. Для этого мы можем использовать модификатор height(IntrinsicSize.Min)
.
height(IntrinsicSize.Min)
определяет размеры своих дочерних элементов, которые должны быть такими же высокими, как их минимальный внутренний рост. Поскольку он рекурсивный, он будет запрашивать Row
и его дочерние элементы minIntrinsicHeight
.
Применив это к нашему коду, он будет работать так, как ожидалось:
@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") } } }
С предварительным просмотром:
minIntrinsicHeight
составного элемента Row
будет максимальным minIntrinsicHeight
его дочерних элементов. minIntrinsicHeight
элемента Divider
равен 0, поскольку он не занимает места, если не заданы никакие ограничения; Text
minIntrinsicHeight
будет соответствовать тексту определенной width
. Таким образом, ограничение height
элемента Row
будет максимальным minIntrinsicHeight
для Text
s. Затем Divider
увеличит свою height
до ограничения height
заданного Row
.
Внутренние особенности ваших пользовательских макетов
При создании пользовательского Layout
или модификатора layout
внутренние измерения рассчитываются автоматически на основе приближений. Поэтому расчеты могут быть не верными для всех планировок. Эти API предлагают возможности переопределить эти значения по умолчанию.
Чтобы указать внутренние измерения вашего пользовательского Layout
, переопределите minIntrinsicWidth
, minIntrinsicHeight
, maxIntrinsicWidth
и maxIntrinsicHeight
интерфейса 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. } ) }
При создании собственного модификатора layout
переопределите связанные методы в интерфейсе 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. }
Рекомендуется для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Пользовательские макеты {:#custom-layouts }
- Линии выравнивания в Jetpack Compose
- Фазы создания реактивного ранца