Одно из правил Compose заключается в том, что измерение дочерних элементов следует проводить только один раз; двукратное измерение дочерних элементов приводит к исключению во время выполнения. Однако бывают случаи, когда перед измерением дочерних элементов требуется некоторая информация.
Intrinsics позволяет вам опрашивать детей до того, как они будут фактически измерены.
Для составного объекта можно запросить его IntrinsicSize.Min
или IntrinsicSize.Max
:
-
Modifier.width(IntrinsicSize.Min)
— Какая минимальная ширина необходима для корректного отображения контента? -
Modifier.width(IntrinsicSize.Max)
— Какая максимальная ширина необходима для корректного отображения контента? -
Modifier.height(IntrinsicSize.Min)
— Какая минимальная высота необходима для корректного отображения контента? -
Modifier.height(IntrinsicSize.Max)
— Какая максимальная высота необходима для корректного отображения контента?
Например, если вы запрашиваете minIntrinsicHeight
Text
с бесконечными ограничениями width
в пользовательском макете, он возвращает 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 ) VerticalDivider( 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)
устанавливает высоту дочерних элементов, равную их минимальной внутренней высоте. Поскольку этот модификатор рекурсивный, он запрашивает значение minIntrinsicHeight
Row
и её дочерних элементов.
Применение этого модификатора к вашему коду заставляет его работать так, как и ожидалось:
@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") } } }
С предварительным просмотром:
Высота Row
определяется следующим образом:
-
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
- Фазы создания реактивного ранца