Android Palette APIで画像から色を抽出する（ステータスバーの色を画像から変える）

Palette APIを使うとBitmapから簡単に色情報を取得することができるようです。
https://developer.android.com/training/material/palette-colors?hl=ja

公式でも書かれていますが、アルバムカバー画像から色を抽出してレイアウトカラーにしたり、今回のようにステータスバーを変更したりなど、ユーザーに視覚的に印象を与える場面で効果的なようです。

で、実際にどんな感じになるのかを試してみたのですが、とても簡単に使うことができました。

実際に書いてみたサンプルの挙動がこんな感じです。リスト表示されているユーザーを選択して詳細画面へいくと、ヘッダーが表示されてステータスバーがそのヘッダー画像から抽出された色に変更されるような感じです。

画面の色味が統一されてすごくいい感じですね！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/6a7b0a41-093c-a279-d128-8d6e16c0af5e.gif" width="320px" />
今回のサンプルコード
<a href="https://github.com/yasukotelin/AndroidFeatureExample"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/yasukotelin/AndroidFeatureExample.png" width="460px"></a>

## Installation

このページのトップの公式ページのリンクを参照した方がいいですけど、これを書いてる今現在はbuild.gradleに `implementation 'com.android.support:palette-v7:28.0.0'` を追加するだけです。28の部分は自分のcompileSdkVersionに合わせる感じです。たぶんAndroidStudioから勝手に提案されると思います。

```groovy:build.gradle
    android {
      compileSdkVersion 28
      ...
    }

    dependencies {
      ...
      implementation 'com.android.support:palette-v7:28.0.0'
    }
```

## Basic Usage

BitmapからPaletteを生成します。生成したPaletteからMutedColorとVibrantColorを取得することができます。

BitmapからのPalette生成はこれだけ。

```kotlin
val palette = Palette.from(bitmap).generate()
```

BitmapからPaletteを生成するのはコストがかかる処理なので、頻度が高い場合や同じPaletteを使い回す場合は何度も `generate()` しないようにした方がいいみたいですね。

あとは非同期的に処理した方がいいかもしれません。CoroutineでUIスレッドをブロックしない形で呼び出した方が良さそうですね。後述します。

パレットからは、6つのパターンで色情報を抽出することができます。

- Light Vibrant
- Vibrant
- Dark Vibrant
- Light Muted
- Muted
- Dark Muted

Vibrantが「活発な」でMutedが「静かな」なので、まあ、それぞれそんな感じで色を抽出してくれるって感じですかね。。笑？

それぞれ `getMutedColor()` みたいな感じで呼び出します。返り値はColorのIntです。AndroidならそのままColorとして使えるので扱いやすいですね！

```kotlin
palette.getMutedColor(Color.BLACK)
```

色の抽出ができなかった時のために引数にはデフォルトカラーを入れておく必要があります。ここでは適当にBlackをデフォルト値として渡しています。

。。。これだけです。めっちゃ楽。
まとめるとこんな感じで使えます。

```kotlin
fun getMutedColor(bitmap: Bitmap): Int {
    return Palette.from(bitmap).generate().getMutedColor(Color.BLACK)
}
```

## In sample code!
このサンプルコードではUseCase層にPalette API処理の関数を定義しています。

```kotlin:PaletteUseCase
fun getMutedColor(bitmap: Bitmap): Int {
    return Palette.from(bitmap).generate().getMutedColor(Color.BLACK)
}
```

Paletteの生成処理はコストがかかるので非同期的に取得したいところです。サンプルコードではViewModelからCoroutineスコープで呼び出す関数を定義しています。MVVM + LiveDataで書いているので、statusBarColor（Int型のMutableLiveData）にpostValueしています。

```kotlin:ViewModel
fun fetchStatusBarColor(bitmap: Bitmap) = viewModelScope.launch {
    // UIスレッドをブロックしない形で呼んであげる
    _statusBarColor.postValue(paletteUseCase.getMutedColor(bitmap))
}
```

FragmentからViewModelの `fetchStatusBarColor()` を呼び出す部分です。BitmapはリソースIDから取得しています。

```kotlin:Fragment
viewModel.fetchStatusBarColor(BitmapFactory.decodeResource(resources, it.headerResId))
```

`fetchStatusBarColor()` で色を抽出すると `statusBarColor` に変更を通知するので、ObserveしてるFragment側でWindowのステータスバーに `getMutedColor()` で取得したInt値をセットしているだけです。

```kotlin:Fragment
viewModel.statusBarColor.observe(viewLifecycleOwner, Observer { color ->
    activity?.also {
        // 本質的なのはこの1行だけ！
        it.window.statusBarColor = color
    }
})
```

以上でこんな感じで動くようになりました！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166629/6a7b0a41-093c-a279-d128-8d6e16c0af5e.gif" width="320px" />