L'une des règles de Compose est de ne mesurer vos éléments enfants qu'une seule fois. Si vous les mesurez deux fois, une exception d'exécution est générée. Toutefois, il arrive que vous ayez besoin d'informations sur vos éléments enfants avant de les mesurer.
Les fonctionnalités intrinsèques vous permettent d'interroger des éléments enfants avant qu'ils ne soient réellement mesurés.
Dans le cas d'un composable, vous pouvez demander son intrinsicWidth
ou intrinsicHeight
:
(min|max)IntrinsicWidth
: compte tenu de cette largeur, quelle est la largeur minimale/maximale de votre contenu que vous pouvez peindre correctement ?(min|max)IntrinsicHeight
: compte tenu de cette hauteur, quelle est la hauteur minimale/maximale de votre contenu que vous pouvez peindre correctement ?
Par exemple, si vous demandez la minIntrinsicHeight
d'un Text
avec une height
infinie, la height
du Text
est renvoyée comme si le texte était dessiné avec une seule ligne.
Fonctionnalités intrinsèques en action
Supposons que vous souhaitiez créer un composable qui affiche à l'écran deux éléments textuels séparés comme ceci :
Comment allez-vous procéder ? Il peut y avoir une Row
contenant deux éléments Text
qui s'étendent autant que possible et, au milieu, un Divider
. Nous voulons que l'Divider
soit aussi grand que l'Text
le plus grand et fin (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 ) } }
En affichant l'aperçu, on constate que Divider
s'étend sur toute la hauteur de l'écran. Ce n'est pas ce qui était prévu:
Cela est dû au fait que Row
mesure chaque élément enfant séparément et que la hauteur de Text
ne peut pas être utilisée pour limiter le Divider
. L'objectif est que Divider
remplisse l'espace disponible avec une hauteur donnée. Pour cela, vous pouvez utiliser le modificateur height(IntrinsicSize.Min)
.
height(IntrinsicSize.Min)
dimensionne ses éléments enfants en les forçant à être aussi grands que leur hauteur intrinsèque minimale. Compte tenu de sa nature récursive, il va interroger Row
et la minIntrinsicHeight
de ses éléments enfants.
Si l'on applique cela à notre code, on obtiendra le résultat attendu :
@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") } } }
Avec l'aperçu :
La propriété minIntrinsicHeight
du composable Row
correspondra à la valeur minIntrinsicHeight
maximale de ses éléments enfants. La propriété minIntrinsicHeight
de l'élément Divider
est 0, car elle n'occupe pas d'espace si aucune contrainte n'est spécifiée. La propriété minIntrinsicHeight
de Text
correspondra à celle du texte auquel une width
spécifique est donnée. Par conséquent, la contrainte height
de l'élément Row
correspond à la valeur minIntrinsicHeight
maximale des éléments Text
. Divider
va alors étendre sa height
en fonction de la contrainte height
fournie par Row
.
Fonctionnalités intrinsèques dans vos mises en page personnalisées
Lorsque vous créez un modificateur Layout
ou layout
personnalisé, les mesures intrinsèques sont calculées automatiquement en fonction d'approximations. Par conséquent, les calculs ne seront peut-être pas corrects pour toutes les mises en page. Ces API offrent des options pour remplacer ces valeurs par défaut.
Pour spécifier les mesures intrinsèques de votre Layout
personnalisée, remplacez les valeurs minIntrinsicWidth
, minIntrinsicHeight
, maxIntrinsicWidth
et maxIntrinsicHeight
de l'interface MeasurePolicy
lors de sa création.
@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. } ) }
Lorsque vous créez votre modificateur layout
personnalisé, remplacez les méthodes associées dans l'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. }
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Mises en page personnalisées {:#custom-layouts }
- Lignes d'alignement dans Jetpack Compose
- Phases de Jetpack Compose