Compose のルールのひとつとして、子を 1 回しか測定できないことが挙げられます。子を 2 回測定した場合、ランタイム例外がスローされます。ただし、測定する前に子の情報が必要になる場合もあります。
Intrinsic を使用すると、実際に測定する前に子をクエリできます。
コンポーザブルに対して、次のように intrinsicWidth
または intrinsicHeight
を要求できます。
(min|max)IntrinsicWidth
: ある幅の場合に、コンテンツを正しく描画できる最小/最大の幅はいくつでしょうか。(min|max)IntrinsicHeight
: ある高さの場合に、コンテンツを正しく描画できる最小/最大の高さはいくつでしょうか。
たとえば、Text
の minIntrinsicHeight
を無限大の height
で要求した場合、テキストが 1 行で描画されているかのように、Text
の height
が返されます。
Intrinsic の使い方
次のように、2 つのテキストを分割線で区切って画面上に表示するコンポーザブルを作成するとします。
これを実現するには、Row
内に 2 つの 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)
は、子の高さが Intrinsic の最小の高さと同じになるように強制します。この修飾子は再帰的であるため、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") } } }
プレビュー:
Row
コンポーザブルの minIntrinsicHeight
が、子の最大 minIntrinsicHeight
になります。Divider
要素の minIntrinsicHeight
は、制約が設定されていない場合はスペースを占有しないため、0 になります。Text
の minIntrinsicHeight
は、特定の width
が指定されたテキストの minIntrinsicHeight になります。したがって、Row
要素の height
制約が、Text
の最大 minIntrinsicHeight
になります。Divider
は自身の height
を、Row
で指定された height
制約まで拡大します。
カスタム レイアウトでの Intrinsic
カスタムの Layout
修飾子または layout
修飾子を作成すると、近似値に基づいて固有の測定値が自動的に計算されます。このため、すべてのレイアウトで計算が不正確になる場合があります。これらの API には、こうしたデフォルト値をオーバーライドするオプションが用意されています。
カスタム Layout
の組み込み測定値を指定するには、作成時に MeasurePolicy
インターフェースの minIntrinsicWidth
、minIntrinsicHeight
、maxIntrinsicWidth
、maxIntrinsicHeight
をオーバーライドします。
@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 でのアライメント ライン
- Jetpack Compose のフェーズ