😽

[Jetpack Compose] Modiferのborderについて調べてみた

2023/03/18に公開

はじめに

Compose の border の描画位置について調べてみました。
border は compose のサイズに影響するのかななどが分かります。

結論

Compose の border は描画の内部に描画されます。
つまり 5dp の border で 50dp の Box があった場合、大きさは 50dp で変わらず、中身は 40dp(50-5-5)になります。

コードを読む

border 関数の中身を見ると、適用する要素の shape 次第で drawGenericBorderdrawRoundRectBorderdrawRectBorderの分岐があります。これはカスタムボーダー、丸縁の長方形、普通の長方形という感じでしょうか。
一番簡単そうなdrawRectBorderを見てみます。
関数は以下のように呼び出しを行っています。

private fun CacheDrawScope.drawRectBorder(
    brush: Brush,
    topLeft: Offset,
    borderSize: Size,
    fillArea: Boolean,
    strokeWidthPx: Float
): DrawResult {...}

描画される位置が知りたいので、位置に関係しそうなパラメータである、topLeftbroderSizestrokeWidthPxなどを確認します。

まずはstrokeWidthですが、以下の値を渡しています。
以下のことが分かりました。

  • border の width が 0 でも 1px の幅になる!
  • dp→px の変換で少数派切り捨て!
  • width が大きすぎる場合、短辺/2 の大きさになる(溢れたりはしない)
val strokeWidthPx = min(
    if (width == Dp.Hairline) 1f else ceil(width.toPx()),
    ceil(size.minDimension / 2)
)

次はborderSizeです。以下の値を渡しています。(この部分記事の最後の方の図で説明しています。)

val borderSize = Size(
    size.width - strokeWidthPx,
    size.height - strokeWidthPx
)

まずはtopLeftですが、以下の値を渡しています。(この部分記事の最後の方の図で説明しています。)

val halfStroke = strokeWidthPx / 2
val topLeft = Offset(halfStroke, halfStroke)

またfillAreaという boolean も渡しているようで、後で使いそう(使う)ので念のため確認しておきます。
border 幅の二倍が短変のサイズより大きければ、おそらく fillMax で描画せよっていう流れが予測できますね。

val fillArea = (strokeWidthPx * 2) > size.minDimension

引数に何を渡しているかだいたい分かったので、実際に描画するところを見てみましょう。
まぁ当然ですが、drawConotentで設定済みの content を描画、その後に border を描画していることがわかりますね。
fillAreaによって size(全体か border 幅を考慮するか)や style(Fill か Stroke か)が異なることが分かります。
いずれにせよ border をどんな値に設定しようとも、コンテンツ幅より大きくなることはなく、コンテンツの内側に描画されることが分かりました!

val rectTopLeft = if (fillArea) Offset.Zero else topLeft
val size = if (fillArea) size else borderSize
val style = if (fillArea) Fill else Stroke(strokeWidthPx)
return onDrawWithContent {
    drawContent()
    drawRect(
        brush = brush,
        topLeft = rectTopLeft,
        size = size,
        style = style
    )
}

さらに自分がはじめて知ったこととして上記のように Stroke を指定した場合や、他にもdrawLineを指定したとき、その幅は offset の点を中心に左右(または上下)に広がるということです。何が言いたいかというと言葉だと難しいので図で説明します。(ダサい図許して)

まとめ

はい、Jetpack Compose の border に関して、描画位置を調べてみました。ものすごく簡単な内容ですが、調べる前はどうなっているんだろう?だったので、読んで記事にして良かったです。
私は当初 border は要素の外側に描画されたら、要素自体のサイズも変わってしまうのではないか?と思い、つまり、Compose の 3 フェーズの一つである Layout フェーズにも影響してしまうのか?どうなっているんだ??と疑問を持ち、ささっと確認してみました。
もっと Modifier のコード読んで行きたいですね~

GitHubで編集を提案

Discussion