Palette APIを使うとBitmapから簡単に色情報を取得することができるようです。
https://developer.android.com/training/material/palette-colors?hl=ja
公式でも書かれていますが、アルバムカバー画像から色を抽出してレイアウトカラーにしたり、今回のようにステータスバーを変更したりなど、ユーザーに視覚的に印象を与える場面で効果的なようです。
で、実際にどんな感じになるのかを試してみたのですが、とても簡単に使うことができました。
実際に書いてみたサンプルの挙動がこんな感じです。リスト表示されているユーザーを選択して詳細画面へいくと、ヘッダーが表示されてステータスバーがそのヘッダー画像から抽出された色に変更されるような感じです。
画面の色味が統一されてすごくいい感じですね!
今回のサンプルコード
Installation
このページのトップの公式ページのリンクを参照した方がいいですけど、これを書いてる今現在はbuild.gradleに implementation 'com.android.support:palette-v7:28.0.0'
を追加するだけです。28の部分は自分のcompileSdkVersionに合わせる感じです。たぶんAndroidStudioから勝手に提案されると思います。
android {
compileSdkVersion 28
...
}
dependencies {
...
implementation 'com.android.support:palette-v7:28.0.0'
}
Basic Usage
BitmapからPaletteを生成します。生成したPaletteからMutedColorとVibrantColorを取得することができます。
BitmapからのPalette生成はこれだけ。
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として使えるので扱いやすいですね!
palette.getMutedColor(Color.BLACK)
色の抽出ができなかった時のために引数にはデフォルトカラーを入れておく必要があります。ここでは適当にBlackをデフォルト値として渡しています。
。。。これだけです。めっちゃ楽。
まとめるとこんな感じで使えます。
fun getMutedColor(bitmap: Bitmap): Int {
return Palette.from(bitmap).generate().getMutedColor(Color.BLACK)
}
In sample code!
このサンプルコードではUseCase層にPalette API処理の関数を定義しています。
fun getMutedColor(bitmap: Bitmap): Int {
return Palette.from(bitmap).generate().getMutedColor(Color.BLACK)
}
Paletteの生成処理はコストがかかるので非同期的に取得したいところです。サンプルコードではViewModelからCoroutineスコープで呼び出す関数を定義しています。MVVM + LiveDataで書いているので、statusBarColor(Int型のMutableLiveData)にpostValueしています。
fun fetchStatusBarColor(bitmap: Bitmap) = viewModelScope.launch {
// UIスレッドをブロックしない形で呼んであげる
_statusBarColor.postValue(paletteUseCase.getMutedColor(bitmap))
}
FragmentからViewModelの fetchStatusBarColor()
を呼び出す部分です。BitmapはリソースIDから取得しています。
viewModel.fetchStatusBarColor(BitmapFactory.decodeResource(resources, it.headerResId))
fetchStatusBarColor()
で色を抽出すると statusBarColor
に変更を通知するので、ObserveしてるFragment側でWindowのステータスバーに getMutedColor()
で取得したInt値をセットしているだけです。
viewModel.statusBarColor.observe(viewLifecycleOwner, Observer { color ->
activity?.also {
// 本質的なのはこの1行だけ!
it.window.statusBarColor = color
}
})