Jetpack ComposeになってShapeがすごく簡単になった

Jetpack Composeになって、Viewの形状を変えるShape周りがすごく使いやすくなってました。
社内のLT会でした内容をQiitaにもまとめようと思います。

https://docs.google.com/presentation/d/1FbiOuWVctnXz1bvRXuSrexm7qX9muE8CKrvnE6hvUv4/edit#slide=id.p

https://github.com/yasukotelin/JetpackComposeCustomShapeSamples

## 今までのAndroid Viewでは

例えばこういう形状のタグ的なものを作ろうと思ったとき、shapeのxmlリソースを用意してbackgroundに当てる必要がありました。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/485e6317-9738-ffd5-1ba3-4a73f27c207c.png)

```shape_tag_background.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <corners android:radius="16dp" />
  <padding
    android:bottom="0dp"
    android:left="0dp"
    android:right="0dp"
    android:top="0dp" />
  <stroke
    android:width="1dp"
    android:color="@color/blue" />
</shape>
```

Viewの形状を変えるというよりかは、背景のリソースを用意して配置するような感じでした。
正直これはかなり面倒でしたし、XMLのタグを覚えていないので毎回ググったり他から引用したりしてます。

微妙にradiusの値が違うとか、ボーダーの太さが違うとか、色が違うというだけで別名のxmlリソースを用意する必要が発生したりして、普通にViewに対してプロパティでほしい。。と何度も思ったことです（笑）

## Jetpack Compose Default Shape

対してJetpack Composeでは、このshapeがとっても簡単に、扱いやすくなっていました。宣言的UIなのでもちろんxmlを用意する必要はありません。

デフォルトで用意されているshapeは、 `RectangleShape` `CircleShape` `RoundedCornerShape` `CutCornerShape` の4つです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/7b8069eb-b366-f9ba-12ae-8869918c9c68.jpeg" width=320" />

一番上の正四角形はこんな感じのコードになっています。

```kotlin
@Composable
fun Rectangle() {
    Box(
        modifier = Modifier
            .size(100.dp)
            .clip(RectangleShape)
            .background(LightGray)
    )
}
```

Boxのmodifierにshapeを指定しています。Android Viewのときはbackgroundに指定していましたが、`clip()` に指定しています。Boxをこのshapeでくり抜いてねってことですね。
shape用のmodifierが追加されているので、背景色やボーダーの色など、形状に関係のない項目に依存しなくなりました👏

widthを長く指定すれば、検索ボックス的なものの背景も簡単に作れそうですね💡

## Rotate

Shapeではないのですが、Jetpack Composeでは回転（rotate）も簡単です。これに関してはAndroid ViewのほうでもViewのプロパティにあるみたいですね。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/4504476e-bd95-a0ae-05cc-465ea7d30893.jpeg" width="320" />

```kotlin
@Composable
fun RoundedCorner(modifier: Modifier = Modifier) {
    Box(
        modifier = modifier
            .size(100.dp)
            .clip(RoundedCornerShape(10.dp))
            .background(LightGray)
    )
}

@Composable
fun RotatedRoundedCorner() {
    RoundedCorner(modifier = Modifier.rotate(25f))
}
```

## Custom Shape

デフォルトで用意されている形状とは違う形状を用意したい場合はCustom Shapeを作る必要があります。こういうCustom系のものって苦手なんですがShapeのはそう難しくなかったです（複雑な場合は別）

例えば三角形を作りたい場合はこんな感じのようです。

```kotlin
@Composable
fun Triangle(modifier: Modifier = Modifier) {
    val triangleShape = GenericShape { size, _ ->
        moveTo(size.width / 2f, 0f)
        lineTo(size.width, size.height)
        lineTo(0f, size.height)
    }

    Box(
        modifier = modifier
            .size(100.dp)
            .clip(triangleShape)
            .background(LightGray)
    )
}
```

`GenericShape` を使って自作のShapeを定義してあげてます。`moveTo(x, y)` と `lineTo(x, y)` を使って形状を描いてあげるイメージです。

もう一例として、付箋のようなちょっと複雑な形状も作れます。

```kotlin
@Composable
fun CustomCutCorner() {
    val shape = GenericShape { size, _ ->
        lineTo(size.width, 0f)
        val cutSize = size.height / 4
        lineTo(size.width, size.height - cutSize)
        lineTo(size.width - cutSize, size.height)
        lineTo(0f, size.height)
    }

    Box(
        modifier = Modifier
            .width(90.dp)
            .height(100.dp)
            .clip(shape)
            .background(LightGray)
    )
}
```

コードだとイメージが付きづらいと思いますが、流れはこんな感じです。理解してしまえば、わりと応用できそう。
でも曲線が混じってくると難しそうな印象です😅

![custom_shape.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/fd01771a-2c99-5fdd-c968-7712b07482e4.jpeg)

## 最初のタグの例を実装してみる

最後に、最初に挙げたタグっぽいViewをJetpack Composeで実装してみます。

```kotlin
@Composable
fun BorderTag(modifier: Modifier = Modifier) {
  val blue = Color(0xFF07bac6)
  Box(
    contentAlignment = Alignment.Center,
    modifier = modifier
        .height(34.dp)
        .border(width = 1.dp, shape = CircleShape, color = blue)
    ) {
      Text(
        text = "リラックス",
        color = blue,
        fontSize = 14.sp,
        modifier = Modifier.padding(horizontal = 16.dp)
      )
    }
  )
}
```

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/485e6317-9738-ffd5-1ba3-4a73f27c207c.png)

ボーダーの部分はBoxで実装しています。その中にTextViewを配置しています。
ボーダーの描画と形状指定はBoxのmodifierで実装していて、`Modifier.border` を使っています。ボーダーちゃんと用意されてるの嬉しいですよね:joy:

最初から最後までコードなのでXMLのタグ調べる必要もないですし、動的に操作する必要があったとしても悩まずに実装できそうです:heart_eyes: