IntrinsicSizeを使ったレイアウト

Kenji Abe
7 min readJan 21, 2024
Photo by Fleur on Unsplash

レイアウトによっては子のサイズに合わせて親のサイズに制約をかけたいことがあります。そういったケースでは IntrinsicSize.MaxIntrinsicSize.Min で対応できることがあります。

言葉だとうまく説明できない感じなので、例を見ながら確認していきます。

使用例

例えば、以下のようなコンテンツの横幅に合わせて Divider を表示したい場合を考えてみます。

これを単純に実装しようとすると以下のようになると思います。

Column {
Text(text = "Upside Down Cake")
Divider()
Text(text = "Tiramisu")
Divider()
Text(text = "Snow Cone")
Divider()
Text(text = "Red Velvet Cake")
Divider()
}

これ実行すると、想定どおりにはならず Divider が横幅いっぱいに表示されます。( Divider にはデフォルトで fillMaxWidth があるので省略してます)

これは子要素の幅が最大のものに合わせられると正しく表示されることになります。

IntrinsicSize.Max / IntrinsicSize.Min

子要素を最小/最大ものに合わせたい場合は IntrinsicSize.MaxIntrinsicSize.Min を使えます。

Column(
modifier = Modifier.width(IntrinsicSize.Max) // IntrinsicSize.Maxを指定
) {
Text(text = "Upside Down Cake")
Divider()
Text(text = "Tiramisu")
Divider()
Text(text = "Snow Cone")
Divider()
Text(text = "Red Velvet Cake")
Divider()
}

こうすることで、以下のように希望するような表示になります。

これを IntrinsicSize.Max ではなく、 IntrinsicSize.Min を使用すると、逆に子要素を最小のサイズに合わせるようにします。

Text の最小サイズは1単語のサイズになります。そのため、この例だと1単語として一番長い Tiramisu の単語の幅に合わせられることになります。

Column(
modifier = Modifier.width(IntrinsicSize.Min)
) {
// ...
}

使用例2

他の例として、コンテンツの高さに応じて線を引きたいときにも使用できます。

Row(
modifier = Modifier.height(IntrinsicSize.Max) // コンテンツの高さする
) {
Divider(
modifier = Modifier
.width(1.dp)
.fillMaxHeight()
)

Spacer(modifier = Modifier.width(8.dp))

Column {
Text(text = "Upside Down Cake")
Text(text = "Tiramisu")
Text(text = "Snow Cone")
Text(text = "Red Velvet Cake")
}
}

カスタムレイアウト対応

カスタムレイアウトを使用している場合は正しく実装しないとクラッシュすることがあります。

例えば、以下のように layout のModifierを使用してる場合や、 Layout のComposableを使用してるときです。

Row(
modifier = Modifier.height(IntrinsicSize.Max)
) {
// ...

        // layoutのModifierを使用
Box(
modifier = Modifier
.layout { measurable, constraints ->
// ...
}
)

// LayoutのComposable
Layout(
content = {
// ...
}
) { measurables, constraints ->
// ...
}
}

layout のModifierと Layout のComposableで対応が若干異なります。

layout Modifier

そのまま layout Modifierでは対応ができないので、 LayoutModifier を使用する必要があります。この例だと maxIntrinsicHeight をOverrideして対応する値を返します。

Row(
modifier = Modifier.height(IntrinsicSize.Max)
) {
// ...
Box(
modifier = Modifier
.then(
object : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
// layout Modifierと同じ実装
}

override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
                                            // コンテンツが正しく描画できる最大の高さを返す(dpじゃなくてpx)
}
}
)
)
}

Layout Composable

Layout Composableの場合は、 MeasurePolicy を使用して対応します。こちらも同様に maxIntrinsicHeight をOverrideして対応します。

Row(
modifier = Modifier.height(IntrinsicSize.Max)
) {
// ...
Layout(
content = {
// ...
},
measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
// Layoutの通常の実装
}

override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
// コンテンツが正しく描画できる最大の高さを返す(dpじゃなくてpx)
}
}
)
}

参考

--

--

Kenji Abe

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO