ComposeでUIを実装するにあたり、borderを角丸の部分だけに描画するという要件が求められた。
たとえば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.widthやthis.size.heightで幅・高さを取得できる。これを使うことで可変サイズにも対応可能である。その際は単位に気をつけたい。