「年末でめっちゃ忙しいんだわ、3行で頼む」人向け
- Android Pie (9.0) から hardware-accelerated な描画方法が若干増えた
- 影の描画もお手軽にできるようになったので Neumorphism でもやってみましょ
- サンプルコードはこちら android-neumorphism-demo
はじめに
冒頭でも書きましたが 公式ページ によると Android Pie から実はグラフィックス周りが若干強化されました。個人的に setShadowLayer
がサポートされたところが大きいと感じていて、非マテリアルデザインで影を多用するアプリの実装がとても容易になりそうだ、と考えています。
Neumorphism の描画を簡単に実装してみた
最近ちらほら見かける Neumorphism が影を多用したUIということなのでまずは こちらのサイト の模倣を早速実装してみました。サンプルコードは冒頭にリンクを用意してありますのでよかったらみてみてください。
コードの解説とポイント
本当はボタンなどのコンポーネントも作りたかったのですが時間が確保できなかったため、最も基本的なViewのみ作りました。
NeumorphismView
上にも貼った uxdesign.cc のページ から画像を拝借
上記表示を実現するためのパラメータとして下記を用意しています:
名前 | 型 | 説明 |
---|---|---|
bgColor | Int | 上図の Main Background 、fillColor と同じに設定するのが基本そう |
fillColor | Int | 上図の Shape Background |
size | Int | neumorphism.io での Size パラメータ |
shadowOffset | Int | neumorphism.io での Distance パラメータ |
roundCornerRadius | Int | neumorphism.io での Radius パラメータ、 cornerRadius としたかったのですが XML のカスタム属性を設定しようとしたところ、名前がコンフリクトしたので round をつけました |
shadowRadius | Int | neumorphism.io での Blur パラメータ |
lightShadowColor | Int | 上図 Light Shadow の色 |
lightShadowColor | Int | 上図 Dark Shadow の色 |
borderColor | Int | 上図 Optional Border の色 |
lightShadowColor | Int | 上図 Light Shadow の色 |
これらを使って角丸矩形を描画するだけです:
override fun onDraw(canvas: Canvas?) {
canvas ?: return
canvas.drawRect(0f, 0f, width + 0f, height + 0f, backgroundPaint)
val cornerRadius = roundCornerRadius + 0f
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, lightShadowPaint)
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, darkShadowPaint)
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, borderPaint)
}
影は setShadowLayer
を使うだけですね:
private fun updateLightShadow() {
lightShadowPaint.color = fillColor
lightShadowPaint.setShadowLayer(shadowRadius + 0f, 0f -shadowOffset, 0f -shadowOffset, lightShadowColor)
invalidate()
}
ViewModel と ViewBinding を活用できるようカスタム属性も定義したので、今回のように動的にパラメータを切り替えるのも簡単です:
<jp.cubenoy22.neumorphism.NeumorphismView
android:id="@+id/neumorphismView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:size="@{ viewModel.size }"
app:shadowOffset="@{ viewModel.distance }"
app:shadowRadius="@{ viewModel.blur }"
app:roundCornerRadius="@{ viewModel.radius }"
/>
パフォーマンスについて
RecyclerView で NeumorphismView を敷き詰めてみました:
程よいミドルレンジであろう Pixel 3a@Android 11 で試したところ、約30ぐらいのビューが存在している状態になりましたがスクロールしても特に負荷が上がった様子は見られませんでした
また軽く既存の Android 向け Neumorphism のライブラリの実装を探ってみたのですがOreo (8.1) 以下をサポートするためにビューごとに Bitmap へ描画して表示するという手法が取られていましたが、ビューの数が多いとメモリを多く消費しそうな予感がしました (*1, *2) 。最近はRAMをたくさん積んでるから大丈夫なのでしょうか。
ちなみにビットマップ上でのぼかし(blur)についてもいろいろやり方がありますが、今回 StackBlur という軽量(らしい)アルゴリズムを使ってCPUで演算しているライブラリがちょこちょこあるのに気づけたのも面白い発見でした:
- https://github.com/fornewid/neumorphism/blob/master/neumorphism/src/main/java/soup/neumorphism/internal/blur/BlurProvider.kt
- https://github.com/wasabeef/Blurry/blob/main/blurry/src/main/java/jp/wasabeef/blurry/Blur.java
最後に
もっと世の中に Android Pie 以降のデバイスが普及してリッチな描画ができたら幸せになれそうと感じました。終わり。