Uma das regras do Compose é que os filhos precisam ser medidos somente uma vez. Medir os filhos duas vezes gera uma exceção de tempo de execução. No entanto, há momentos em que você precisa de algumas informações sobre os filhos antes de medi-los.
Com a medição intrínseca, é possível consultar os elementos filhos antes que eles sejam realmente medidos.
Para uma função de composição, você pode solicitar intrinsicWidth
ou intrinsicHeight
:
(min|max)IntrinsicWidth
: considerando essa largura, qual é a largura mínima/máxima para que você possa pintar seu conteúdo corretamente?(min|max)IntrinsicHeight
: considerando essa altura, quais são as alturas mínima e máxima para que o conteúdo seja pintado corretamente?
Por exemplo, se você solicitar minIntrinsicHeight
de um Text
com height
infinita, ela vai retornar a height
do Text
como se o texto tivesse sido exibido em uma
única linha.
Medições intrínsecas em ação
Imagine que queremos criar uma função de composição que exiba dois textos na tela, separados por um divisor como este:
Como podemos fazer isso? Podemos ter uma Row
com dois Text
s que se expandem
o máximo possível e um Divider
no meio. Queremos que o Divider
tenha a
mesma altura que o Text
mais alto e seja fino (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 ) } }
Na prévia, veremos que a Divider
se expande para a tela inteira, e
esse não é o resultado esperado:
Isso acontece porque Row
mede cada filho individualmente, e a altura de
Text
não pode ser usada para limitar o Divider
. O objetivo é que Divider
preencha
o espaço disponível com uma altura definida. Para isso, podemos usar o
modificador height(IntrinsicSize.Min)
height(IntrinsicSize.Min)
dimensiona os filhos, para que a altura deles seja igual
à altura intrínseca mínima. Por ser recursivo, ele consultará a minIntrinsicHeight
da Row
e das filhas dela.
Aplicando isso ao código, ele funcionará como esperado:
@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") } } }
Com a visualização:
A minIntrinsicHeight
que pode ser composta da Row
será a
minIntrinsicHeight
máxima das filhas dela. O minIntrinsicHeight
do
elemento Divider
é 0 porque não ocupa espaço quando nenhuma restrição é
estabelecida. A minIntrinsicHeight
do Text
será igual ao do texto que recebeu uma width
específica. Portanto, a restrição de height
do elemento Row
será a minIntrinsicHeight
máxima dos Text
s. O Divider
expandirá a height
dele para
a restrição de height
especificada pela Row
.
Medições intrínsecas nos layouts personalizados
Ao criar um modificador Layout
ou layout
personalizado, as medições intrínsecas
são calculadas automaticamente com base nas aproximações. Portanto, os
cálculos podem não estar corretos para todos os layouts. Essas APIs oferecem opções
para substituir esses padrões.
Para especificar as medidas intrínsecas do Layout
personalizado,
modifique a minIntrinsicWidth
, a minIntrinsicHeight
, a maxIntrinsicWidth
e a maxIntrinsicHeight
da interface
MeasurePolicy
durante a criação dela.
@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. } ) }
Ao criar o modificador layout
personalizado, substitua
os métodos relacionados na interface 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. }
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Layouts personalizados {:#custom-layouts }
- Linhas de alinhamento no Jetpack Compose
- Fases do Jetpack Compose