Jetpack ComposeでカスタムShapeを実装する

Kenji Abe
5 min readMay 28, 2023
Photo by Shubham Dhage on Unsplash

Jetpack Composeには Shape というのがあり、これを使って、画像を切り抜いたり、背景色を塗ったり、ボーダーを描いたりすることができます。

Compose側では CircleShape , RoundedCornerShape , CutCornerShape などが定義されていますので、基本的なものはこれらで対応可能になっています。

もし、定義されていないShapeを使用したい場合は、自分でカスタムのShapeも実装することが可能です。

カスタムShapeの実装

カスタムShapeを実装するには、 GenericShape を使うことで簡単に実装することができます。

val CustomShape = GenericShape { size, layoutDirection ->
// 三角形の頂点へ移動
moveTo(size.width / 2f, 0f)
// 頂点から右下に向かって線を引く
lineTo(size.width, size.height)
// 右下から左に向かって線を引く
lineTo(0f, size.height)
// GenericShapeのほうで close() が呼ばれるので、最後に線が頂点で閉じる
}

コンストラクタで Path をレシーバーにしたbuilderを渡すことができるようになっています。このbuilderの中でPathを組み立てることでカスタムのShapeを実装することができます。

この例では三角形のカスタムShapeを実装しています。 コメントに何をやってるか簡単に書いておきました。

このカスタムShapeを使う場合は特別なことは特に不要で、通常通り使用するだけで大丈夫です。

Image(
painter = painterResource(R.drawable.sample),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(200.dp)
.clip(CustomShape) // 作成したカスタムShapeで切り抜く
)

これを実行すると以下のように、作成したカスタムShapeで画像が切り抜かれます。

別の方法としては、 Shape を直接実装する方法もあります。こちらは

class CustomShape : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val path = Path().apply {
moveTo(size.width / 2f, 0f)
lineTo(size.width, size.height)
lineTo(0f, size.height)
close()
}
return Outline.Generic(path)
}
}

先程と同じShapeを実装しています。 createOutline メソッドを実装して、 Outline を返却する感じになります。 今回はカスタムのPathを使いたいので Outline.Generic を使っています。

使用する場合は、先程とほぼ同じですが、classなのでインスタンスを作成する必要があります。

Image(
painter = painterResource(R.drawable.sample),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(200.dp)
.clip(CustomShape()) // インスタンスの作成が必要
)

GenericShape vs Shape

カスタムShapeを実装する方法を2つ紹介しましたが、どちらが良いかはパラメータによって形を変えるかどうかによります。

GenericShape ではパラメータを外部から受け取ることができないので毎回決まった形になります。もし、何かパラメータを受け取って形を変えたい場合は Shape を直接実装してコンストラクタでパラメータを受け取ると良いと思います。

Shape を使ったときの実装ですが、1点注意があって、 equals が実装されていないので、Recompositionがskipされなくなります。なので、自分で equals を実装する必要があります。このあたりは 既存の実装 などを参考にしてみると良いと思います。
GenericShape に関しては実装されてるので、自分で実装する必要はないです。

--

--

Kenji Abe

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