【Android】MaterialComponentsのComponent別Badge付け方まとめ

# はじめに


マテリアルデザインの公式ライブラリ「Material Components Android」ですが、新機能が続々と登場しています。今回はその中のBadgeにテーマを絞って、Component別のBadgeの実装方法について説明していきます。

# Badgeとは

Badgeとは、通知の件数表示などに使われるコンポーネントです。
Badgeには以下のように三種類の表示形式があり、これらを実装するためのAPIが公式のMaterial Components Androidで提供されています。

<img width="717" alt="Badgeの種類" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147934/00661aa2-def4-8a1e-8fbd-5a949aee1c91.png">

Badgeについてはこちらの公式ドキュメントにて詳しく解説されています。

[BottomNavigation | Material Design](https://material.io/design/components/bottom-navigation.html#behavior)

## Badgeの種類

Badgeの種類は以下の3種類になります。

### 1. ドット

デフォルトでの表示になります。

<img width="157" alt="ドット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147934/93b268cf-a8ad-b01a-ab68-32ee9947b85c.png">

### 2. 数字付き

通知の未読件数を表示したい時などに便利です、

<img width="146" alt="数字付き" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147934/3f9a4a52-8cdf-deaf-6862-fc27331f8f81.png">

### 3. 数字付き（最大値を超えた場合）

指定した最大文字数を超えると、以下のように`+`マークが後ろに付与されます。
何も指定しなくても最大値は設定されていて、デフォルトでは3桁（999）が最大になります。

<img width="146" alt="最大値" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147934/d6d2d3c1-4e55-12d1-599b-49439f10fa7b.png">

## BadgeDrawable

Material Components AndroidでBadgeを実装するにはBadgeDrawableというクラスを使って実装します。公式のドキュメントは以下になります。

[BadgeDrawable | Material Design](https://material.io/develop/android/components/badging/)

今の所こちらのBadgeDrawableによるBadge表示のAPIを提供しているコンポーネントは以下になります。これらのコンポーネントについては、バッジを付与するための独自のカスタムビューなどを作成せずともバッジの実装が可能になります。（※material-component-android-1.1.0-alpha10時点）

- BottomNavigation
- TabLayout

今後もMaterialComponentは新機能が追加されていく可能性があるので、こちらのリポジトリをwatchしておくと良さそうです。

https://github.com/material-components/material-components-android

# 下準備

では早速実装方法について見ていきます。まずは下準備として`build.gradle`にdependenciesを追加します。

## `app/build.gradle`のdependenciesを追加

まず、app配下のbuild.gradleにdependenciesを追加します。今回は、現時点での最新の`materialcomponents1.1.0-alpha10`を使用します。

```gradle:app/build.gradle
// 省略

dependencies {
  ...
  implementation 'com.google.android.material:material:1.1.0-alpha10' // こちらを追加
}
```

## アプリのThemeは`Theme.MaterialComponents`を継承したものを使う

MaterialComponentsを使用するためには、アプリのThemeに`Theme.MaterialComponents.*`のものを指定しなければなりません。

例えば、以下のようにThemeを設定する必要があります。

```xml:res/themes.xml
  <!-- Theme.AppCompat.Light.DarkActionBar ではない -->
  <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
    ...
  </style>
```

# コンポーネント別Badgeの付与方法

## BottomNavigation

### Badgeの表示

`getOrCreateBadge(menuItemId)`メソッドを使います。
引数には、BottomNavigationに設定されているmenuItemの中で、表示したいitemIdを指定します。
※1.1.0-alpha08で、`showBadge()`からリネームされているので要注意です。

`getBadge(menuItemId)`というメソッドもありますが、こちらは取得のみのメソッドなので、バッジがmenuItemにアタッチされていない場合もあります。そのため、返り値がNullableです。NonNullで取得したい場合は`getOrCreateBadge()`を使うのが良さそうです。

### Badgeの非表示

`removeBadge(menuItemId)`メソッドを使います。
引数には、BottomNavigationに設定されているmenuItemの中で、非表示にしたいitemIdを指定します。

```kotlin

// 表示
val notificationMenuItem = binding.bottomNav.menu.item(NOTIFICATION_ITEM_ID) // Badgeを付与したいmenuItemを取得
notificationMenuItem?.let {
    binding.bottomNav.orCreateBadge(it.itemId)
}

// 非表示
notificationMenuItem?.let {
    binding.bottomNav.removeBadge(it.itemId)
}
```

## TabLayout

### Badgeの表示

TabLayoutのTabクラスにBadgeDrawableを使用するためのAPIが提供されています。
`tab.getOrCreateBadge()`メソッドを使って、BadgeDrawableを作成します。
Kotlinで書くとプロパティアクセス的に書けてしまうので、実際には`tab.orCreateBadge`と書くことになります。
※1.1.0-alpha08で、`showBadge()`からリネームされているようですので要注意です。

また、バッジの取得には`getBadge()`メソッドがありますが、こちらはnullableアノテーションがついているので、nonnullで扱いたい場合は、基本的に`getOrCreateBadge()`を使うのが良さそうです。

### Badgeの非表示

`removeBadge()`メソッドを使います。

```kotlin

// 表示
val badge = tab.orCreateBadge

// 非表示
tab.removeBadge()
```

# Badgeのオプション設定

基本的にどちらのコンポーネントでもBadgeDrawableを取得した後のオプション設定方法は共通なので、こちらでまとめて紹介していきます。

## 数字の設定

数字の付与には`setNumber()`メソッドを使います。

```kotlin

val notificationMenuItem = binding.bottomNav.menu.item(NOTIFICATION_ITEM_ID)
notificationMenuItem?.let {
    val badge = binding.bottomNav.orCreateBadge(it.itemId)
    badge.number = 10 // バッジに表示する数字の付与
}
```

`clearNumber()`というメソッドもありますが、こちらは、数字付きのBadgeが数字なしのBadge表示に変わるだけなので、普通にバッジを削除したいのであれば、Badgeがついている親Viewに実装されている`removeBadge()`を呼び出しましょう。

## 最大値の設定

最大値の設定はバッジに表示される文字数で指定します。つまり`+`文字がデフォルトで入るので、（最大桁数+1）の数字になります。
`setMaxCharacterCount()`を使って、Badgeの**最大表示文字数**を指定します。

```kotlin

val notificationMenuItem = binding.bottomNav.menu.item(NOTIFICATION_ITEM_ID) // Badgeを付与したいmenuItemを取得
notificationMenuItem?.let {
    val badge = binding.bottomNav.orCreateBadge(it.itemId)
    badge.number = data.count()
    badge.maxCharacterCount = 3 // 最大表示文字数の指定。この場合99が最大件数。
```

## 色の設定

バッジの背景色は、`setBackgroundColor()`メソッドを使います。
バッジのテキストの色は、`setBadgeTextColor()`メソッドを使います。

```kotlin

val notificationMenuItem = binding.bottomNav.menu.item(NOTIFICATION_ITEM_ID)
notificationMenuItem?.let {
    // ↓applyを使った方が綺麗に書けます。
    val badge = binding.bottomNav.orCreateBadge(it.itemId)
    badge.backgroundColor 
        = ContextCompat.getColor(context, R.color.bottom_nav_badge_background_red) // 背景色の指定
    badge.badgeTextColor 
        = ContextCompat.getColor(context, R.color.bottom_nav_badge_text_white) // テキストの色の指定
```

# まとめ

公式のライブラリでバッジをサポートしてくれるとコードの記述量を削減できますし、マテリアルデザインに沿って、レイアウトしてくれるのでとてもありがたいです。
今後もバージョンアップがあれば更新していこうと思います。最後までご覧いただきありがとうございました。