【Android】アプリにMaterial DesignのShapeを適用して角丸デザインにする

# 自己紹介
個人でアプリ開発を行なっている[うめちゃん](https://umechanhika.github.io/)です。
今回は私が作っている、日々の良かったことを記録して貯めておけるメモアプリ[HappinessBank](https://play.google.com/store/apps/details?id=com.umehika.happinessbank&hl=ja)に、Material DesignのShapeを適用したのでその紹介をしたいと思います。
また、Shapeの概要を簡単に説明していますのでそちらもご参照ください。（内容を掻い摘んでいるため、詳しく知りたい方は各項目に記載のリンク先をご参照ください。）

# 背景
HappinessBankは、良かったことやしあわせなことを記録しておけるメモアプリです。
アプリのコンセプトである良かったことやしあわせなことって、なんとなく温かさや柔らかさがあるなぁという印象を持っていました。
なので、アプリは丸みを帯びた柔らかいデザインで表現したいと思いました。

ファーストリリース時はMaterial Components for Androidが`1.0.0-rc01`の頃で、その時はShapeを使わずに角丸を実装していました。
しかし、どこかのバージョンからその実装では角丸が表現できなくなってしまいました。
なので、今回はShapeを使って角丸デザインにリベンジしたという感じです。

# ガイドライン
## Shapeの使いどころ
https://material.io/design/shape/about-shape.html より

* 強調するとき
    * 例）四角を基調としたレイアウトの中に丸みを帯びたボタンを配置して強調する
* 同一性を表すとき
    * 例）タグを同じ角丸サイズのChipで表すことで同一性を表す
* 状態を表すとき
    * 例）選択されたアイテムだけ角丸にする
* ブランディングするとき
    * 例）ブランドカラーのように、アプリ内で統一したブランドシェイプを用いてブランディングする

## ShapeをUIに適用する
https://material.io/design/shape/applying-shape-to-ui.html より

### コンポーネントのカテゴリ

* Small Components
  * Button, FAB, Snackbar, Expanding bottom sheet(collapsed), etc...
* Medium Components
  * Card, Dialog, Image list item, etc...
* Large Components
  * Expanding bottom sheet(expanded/full-screen), Nav drawer, etc...

### カスタマイズできる要素

* Shape family
  * 角丸(Round) or 切り欠き(Cut)
* Size
  * コーナーサイズ。コンポーネントによってmin/maxが異なる。

### カスタマイズできないコンポーネント
TopAppBarやCheckbox、全画面表示のBottomSheetなどは、識別しにくくなったり操作しにくくなる可能性があるため、カスタマイズできません。

## Androidの実装の話
https://material.io/develop/android/theming/shape/ より

### カテゴリ一括で適用

```xml:styles.xml
<!-- アプリのテーマ -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
  ...
  <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.MyApp.SmallComponent</item>
  <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.MyApp.MediumComponent</item>
  <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.MyApp.LargeComponent</item>
  ...
</style>

<!-- 各コンポーネントのShapeの定義 -->
<style name="ShapeAppearance.MyApp.SmallComponent" parent="ShapeAppearance.MaterialComponents.SmallComponent">
  <item name="cornerFamily">cut</item>
  <item name="cornerSize">4dp</item>
</style>

<style name="ShapeAppearance.MyApp.MediumComponent" parent="ShapeAppearance.MaterialComponents.MediumComponent">
  <item name="cornerFamily">cut</item>
  <item name="cornerSize">8dp</item>
</style>

<style name="ShapeAppearance.MyApp.LargeComponent" parent="ShapeAppearance.MaterialComponents.LargeComponent">
  <item name="cornerFamily">rounded</item>
  <item name="cornerSize">4dp</item>
</style>
```

### コンポーネント一括で適用

```xml:styles.xml
<!-- アプリのテーマ -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
  ...
  <item name="materialCardViewStyle">@style/Widget.MyApp.MaterialCardView</item>
  ...
</style>

<!-- コンポーネントのスタイル -->
<style name="Widget.MyApp.MaterialCardView" parent="Widget.MaterialComponents.MaterialCardView">
  <item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.MyApp.MaterialCardView</item>
</style>

<!-- コンポーネントのShapeの定義 -->
<style name="ShapeAppearanceOverlay.MyApp.MaterialCardView" parent="">
  <item name="cornerFamily">rounded</item>
  <item name="cornerSize">16dp</item>
</style>
```

### 各View個別で適用

```xml:styles.xml
<!-- コンポーネントのShapeの定義 -->
<style name="ShapeAppearanceOverlay.MyApp.MaterialCardView.Cut" parent="">
  <item name="cornerFamily">cut</item>
</style>
```

```xml:layout.xml
<!-- レイアウトのapp:shapeAppearanceOverlayにShapeの定義を指定 -->
<com.google.android.material.card.MaterialCardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/mtrl_card_spacing"
    android:layout_marginTop="@dimen/mtrl_card_spacing"
    android:layout_marginRight="@dimen/mtrl_card_spacing"
    app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MyApp.MaterialCardView.Cut">
  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/card_text"/>
</com.google.android.material.card.MaterialCardView>
```

# アプリに適用する
実際にHappinessBankに適用してみたので、Before/Afterの形式でご紹介します。
当初の目的だった柔らかさの表現が、BeforeよりAfterの方が増していると思います。

## FABとBottomAppBarの接続部を角丸にする
◆Before
![bottom_app_before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/aaad51e9-a5c9-2628-7580-1bf621a452b7.png)
◆After
![bottom_app_after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/c0748970-8071-39f2-d2df-1caaee13e754.png)

## BottomSheetを角丸にする
◆Before
![bottom_sheet_before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/9c77daaa-d42d-ac61-72ec-fac27402cfcb.png)
◆After
![bottom_sheet_after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/bb1c6fee-c563-e4ea-ec2e-6e75ee3fade6.png)

## ボタンとテキストボックスを角丸にする
◆Before
![text_before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/53b4e138-56d5-69fe-b34d-d9109c1c2919.png)
◆After
![text_after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/e1c115e7-73b7-34fd-6390-7146145dfe85.png)

# さいごに
今回紹介したアプリはPlayストアで公開しているので、是非手にとって見た目を確認してみてください。
最後まで読んでいただき、ありがとうございました。
https://play.google.com/store/apps/details?id=com.umehika.happinessbank&hl=ja

# Material Designに関連する記事
Material Components for AndroidのSnackbar表示位置に関する不具合に遭遇した時の話をしています。
https://qiita.com/umechanhika/items/ea8acb15dd38f705e398