Composeでborderの一部分を区切ったようなエフェクトをつける

ComposeでUIを実装するにあたり、borderを角丸の部分だけに描画するという要件が求められた。

drawBehindでpathEffectを使う

たとえばBoxにborderをつける場合、Modifierにborderを指定すればborderをつけることができる。ただしこれでは単純なborderしか表現できない。

ところで点線で描画するといった手法であれば、PathEffectを使えば実現できる。これをうまく利用すれば角丸部分だけのborderが表現できそうだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    Box(
        modifier = modifier
            .size(50.dp)
            .background(color = Color.Red)
            .drawBehind {
                // floatで指定はpxの値なので、dp.toPx()でdpの値にして更にpxにする
                // cornerRadiusは x=8.dp, y=8.dpで指定するので、実際のコーナーの長さは直径は16*PI/4になる
                val cornerLength = (16 * PI / 4).dp.toPx()
                val effect = PathEffect.dashPathEffect(
                    floatArrayOf(
                        0.dp.toPx(),
                        34.dp.toPx(), // Boxのサイズが50で、角丸部分のyの高さが8*2なので、残りが直線部分
                        cornerLength,
                        34.dp.toPx(),
                        cornerLength,
                        34.dp.toPx(),
                        cornerLength,
                        34.dp.toPx(),
                        cornerLength,
                        0f,
                    ),
                )

                drawRoundRect(
                    cornerRadius = CornerRadius(8.dp.toPx(), 8.dp.toPx()),
                    color = Color.Black,
                    style = Stroke(
                        width = 1.dp.toPx(),
                        pathEffect = effect,
                    )
                )
            }
        ,
    )

drawRoundRectで描画する際に、はじめの位置は左下の直線部分からの指定になる。これがdrawRectの場合は左上からの指定になる。ちょっとややこしい。

pathEffectで指定するFloatArrayは必ず2個1セットで指定する。奇数個だからといってエラーにはならないが、最後の指定が無視されてしまう。FloatArrayの指定は、描画する・しない(オンとオフ)の組になる。

phaseの指定もできる。phaseはFloatArrayの指定のどこから始めるかを指定する。

ここでは50.dpと固定のサイズでやったが、可変サイズにも対応可能である。

drawBehindのDrawScope内では、this.size.widththis.size.heightで幅・高さを取得できる。これを使うことで可変サイズにも対応可能である。その際は単位に気をつけたい。

Amazonのほしいものリストを公開しています。仕事で欲しいもの、単なる趣味としてほしいもの、リフレッシュのために欲しいものなどを登録しています。 寄贈いただけると泣いて喜びます。大したお礼はできませんが、よりよい情報発信へのモチベーションに繋がりますので、ご検討いただければ幸いです。