こんにちは。dely開発部にてクラシルのAndroidエンジニアを担当しているnozaです。 月日の流れは早いもので、前回の記事から間があいてしまいましたね。
先月、Material Components for Androidのバージョン1.3.0*1が公開されましたね。 主な内容として下記のComponentが追加されてます。
- MaterialTimePicker
- ProgressIndicator
個人的にはProgressIndicatorを待ち望んでいました。
というのも、Android SDKで提供されているProgressBarだと、思った通りに手軽にプログレス表示するのが難しかったからです。
今回はProgressBarのモヤっとポイントと、ProgressIndicatorだとどんなプログレス表示が実現できるのかを紹介していきます。
ProgressBar*2
Android SDKで提供されるもので、標準の設定で円形にしたり水平バーにしたりできます。 画面内のコンテンツを取得するための通信中などに用いることが多いと思います。
Android版クラシルでは、画面内のコンテンツ表示の邪魔をしないようにToolbarの下にバーの形状で設置しようとしました。
しかしながら、お手軽に実現できないモヤっとポイントがいくつかあったのです。
ProgressBarのモヤっとポイント
バーの上下の余白
indeterminate=true
の場合、標準の設定で下記のようなアニメーションが実現できます。
しかし、このアニメーションで設定されるVectorDrawable(API Level21未満だとpng画像)自体に上下の余白が入っています。 Viewの範囲をわかりやすくするために背景色#ddddddを入れてみると・・・
このようにViewの描画領域とバーの間に余白があることがわかります。
意図した場所に配置するためにはこの余白を考慮して工夫する必要がありました。
工夫の例をいくつか挙げてみます。
例1:自前でDrawableを用意して、余白をコントロールする。
標準で用意されているDrawableに余白が含まれているなら、自前で作成してしまえば良いです。 が、VectorDrawableやアニメーションなど用意するものが多く、ある程度知識も必要になるため面倒くさいです。
※progressDrawable
、indeterminateDrawable
というattributeで設定可能
例2:上下のマージンに程よいマイナス値を設定してごまかす
<ProgressBar style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="-6dp" android:layout_marginBottom="-6dp" android:indeterminate="true" />
marginにマイナス値を設定することで余白分を無くしています。
が、小手先で対応している感じがあってモヤっとします。
例3:layout_heightとscaleYでごまかす
<ProgressBar style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="4dp" android:indeterminate="true" android:scaleY="4" />
高さ4dp
にすることで、バーの表示が縮んでしまうのでscaleY="4"
で引き伸ばしています。
上下に余計な余白は入っていませんが、縮めて引き伸ばしているためボケてしまっています。 かっこ悪いですね。
といった感じで、お手軽でしっくりくる解決ができずモヤっとしていました。
表示/非表示時の切り替え
例えば通信中にはProgressBarを表示し、通信完了したら非表示にしたいと思います。 よくやる方法としてはvisibilityを操作する方法ですが、それだとパッと切り替わってしまうためチカチカした印象になります。
突然何かが表示されたり消えたりすると潜在的な違和感を与えてしまうので、もっと滑らかに出たり消えたりさせたいものです。 表示状態を切り替える時にアニメーションをうまいこと実行させるのも手ですが、お手軽に実現したいというモヤっと感がありました。
そこで、Material ComponentのProgressIndicatorの出番です。
Material ComponentのProgressIndicator
前述したモヤっと感を、Material ComponentのProgressIndicatorを利用して解決してみましょう。
水平バーで表現できるLinearProgressIndicator
を使います。
<com.google.android.material.progressindicator.LinearProgressIndicator android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminate="true" app:hideAnimationBehavior="outward" app:showAnimationBehavior="inward" app:indicatorColor="#ffaa4e" app:trackColor="#eeebe7" />
上記のxmlで定義したLinearProgressIndicatorをlinearProgressIndicator
という変数名で取得しておきつつ・・・
// 表示したい時に呼び出す linearProgressIndicator.show() // 非表示にしたい時に呼び出す linearProgressIndicator.hide()
※Material Componentの導入*3は省略しています。
はい、これだけでできました!
滑らかで美しい!
ProgressIndicatorで設定可能なattributes
Material DesignのProgress indicators*4にどんな風に扱えるのか記載がありますが、どんな見た目になるのかやってみましょう。
色
進捗を表す部分はindicatorColor
で、その下地はtrackColor
で色を設定できます。
また、indicatorColor
には色の配列を設定可能です。
<!-- colors.xml に下記を定義 --> <array name="progress_colors"> <item>#f00</item> <item>#ff0</item> <item>#0f0</item> <item>#00f</item> </array>
<com.google.android.material.progressindicator.LinearProgressIndicator android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminate="true" app:indicatorColor="@array/progress_colors" />
indeterminateな水平バーを使用する場合、indeterminateAnimationType
というattributeで、色配列がどのように適応されるのかを指定できます。
indeterminate AnimationType |
見た目 |
---|---|
disjoint | |
contiguous |
ただし、"contiguous"という設定を使用するには条件がいくつかあるので注意です。
trackCornerRadius
(後に説明)は設定不可indicatorColor
で設定した色が3色以上- 配列が3つ以上でも、色が3色以上である必要がある
※設定を無視してくれればいいのですが、レイアウトxmlの読み込みでエラーになりクラッシュしました。
形
trackThickness
で高さ(太さ?)を、
trackCornerRadius
で、indicator部分に丸みを設定できます。
"丸み"とはなんなのかは、実際の表示をみてもらうとわかりやすいです。
(よりわかりやすくするためにtrackThickness
を1増してます)
見た目 | |
---|---|
trackCornerRadius設定なし | |
trackCornerRadius設定あり |
表示アニメーション
表示時はapp:hideAnimationBehavior
、非表示時はapp:showAnimationBehavior
のattributeで設定できます、
ProgressIndicatorは標準ではnone
が設定されていて、アニメーションがありません。
設定値 | 非表示時(hide) | 表示時(show) |
---|---|---|
none | 標準で設定されているもの。アニメーションしない。 | 標準で設定されているもの。アニメーションしない。 |
outward | 下端から上端に向かって折りたたまれる | 下端から上端まで拡大 |
inward | 上端から下端に向かって折りたたまれる | 上端から下端まで拡大 |
それぞれを設定してみた時の見た目はこんな感じです。
hide | show | 見た目 |
---|---|---|
outward | outward | |
outward | inward | |
inward | outward | |
inward | inward |
ちなみに表示アニメーションが実行された後はvisibilityが変更されます。
show()
の後にはView.VISIBLE
が、hide()
の後にはView.INVISIBLE
かView.GONE
となりますが、これはsetVisibilityAfterHide(int visibility)
というメソッドで設定できます。
アニメーション方向
標準では左から右へ満たされたり流れたりするアニメーションですがapp:indicatorDirectionLinear
というattributeで変更可能です。
determinateな場合
indicatorDirectionLinear | 見た目 |
---|---|
leftToRight | |
rightToLeft |
indeterminateな場合
indicatorDirectionLinear | 見た目 |
---|---|
leftToRight | |
rightToLeft |
※startToEnd
、endToStart
も設定可能だが省略
※LinearProgressIndicatorだけに用意されたattribute
他にもCircularProgressIndicator
用に用意されたattributeもあるのですが、もりもりになってきたのでひとまずここまで。
ProgressBar、ProgressIndicatorの使い所
ProgressIndicatorの紹介をしてきましたが、どんなプログレス表示をしたいかによってProgressBarを使うのか、ProgressIndicatorを使うのかは変わってくると思います。
ProgressIndicatorはMaterialDesignな見た目の表示はお手軽に実装できますが、独自のアニメーション(例えばロゴがグルグル回ったり跳ねたり・・・)を設定するattributeは用意されていません。 凝ったプログレス表示をしたい場合は、Drawableを自前で作成してProgressBarに適応した方が良さそうです。 筆者は今回ProgressBarの中身も覗いてみたのですが、アニメーションの設定とか楽しそうだなと思いました。
おわりに
MaterialComponentのProgressIndicatorについて紹介してきましたが、Android版クラシルでも実際に導入しています。
Android版クラシルでは、読み込み中の表示から通信中、コンテンツ取得後の表示までがなめらかでユーザーの目に優しいものになっていると思います。
それはサービスに大きく関わるような効果ではありませんが、アプリ全体を通じて「安心感」や「温かみ」のようなものをユーザへ与えられているのではないかなと思います。
極端な例を用意してみましたが、どう感じますか?
新しい機能を通じてユーザーに最適で最大の価値を提供する事はもちろん大切なことですが、このような細かなところへの配慮を重ねることでも快適さを実現していきたいですね。
*1:Material Components for Android v1.3.0:
https://github.com/material-components/material-components-android/releases/tag/1.3.0
*2:ProgressBarの公式ドキュメント:
https://developer.android.com/reference/android/widget/ProgressBar
*3:Getting started with Material Components for Android:
https://github.com/material-components/material-components-android/blob/master/docs/getting-started.md
*4:Material Design Progress indicators:
https://material.io/components/progress-indicators/android