【移行のお知らせ】プライバシー上の懸念により、Qiitaアカウントの削除を検討しています。
各投稿は個人blog( https://gateau.hatenablog.com/archive/category/Qiita )に移行しました。
今後も参照される方は、ブックマークなど移行をお願い致します。
Lottie( http://airbnb.io/lottie/ )はAirbnb社が開発・公開しているアニメーションライブラリです。
アニメーションのjsonさえあれば、ImageViewと似た指定で簡単に扱えます。サンプルアプリ を入れてみると挙動がわかりやすいです。
jsonの作り方は他のドキュメントに任せて、Androidの実装側で色々やってみた例を紹介します。
きほん
アプリ内のassetsなどに置いたjsonを読み込む
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_fileName="hello_world.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
アプリ内に置かず、URLで指定する
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_url="https://xxxxx/hello_world.json" />
もっと!
可変なURLを指定する
サーバからjsonのURLを取得して、その内容を表示させたいとき。これはxmlだけでは出来なくて、コードを書くことになります。
lottieView.setAnimationFromUrl(url)
この指定で表示はされます。しかし、URLが存在しなかったりロード出来なかったときに IllegalStateException が投げられてしまいます。
たぶんこの部分 https://github.com/airbnb/lottie-android/blob/master/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java#L66
private final LottieListener<Throwable> failureListener = new LottieListener<Throwable>() {
@Override public void onResult(Throwable throwable) {
throw new IllegalStateException("Unable to parse composition", throwable);
}
};
落ちないように次のようにしました。
LottieCompositionFactory.fromUrl(this, url)
.addFailureListener {
// 読み込み失敗時の処理
}
.addListener {
lottieView.setComposition(it)
}
コードで動的に指定するときは lottieView.playAnimation()
を忘れるとアニメーションが開始しないので注意です。
アニメーションが終わったら消す
AnimatorListenerかAnimatorListenerAdapterのListenerをセットし、アニメーションの開始や終了時の処理を記述できます。
lottieView.addAnimatorListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
lottieView.visibility = View.GONE
}
})
複数のアニメーションを連続して表示する
サーバから1つずつ表示したいアニメーションのURLを取得し、それをQueueに詰めてみることにしました。
private val lottieQueue: LinkedList<String> = LinkedList()
private val animationListener = object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animator: Animator) {
playNextLottie()
}
}
// 〜〜〜
{ // どこかでサーバから1つずつ取得する
lottieQueue.add(enqueueURL) // 複数詰める
playNextLottie()
}
// 〜〜〜
private fun playNextLottie() {
val lottieUrl = lottieQueue.pollFirst()
if (lottieUrl.isNullOrEmpty()) { // Queueから全てなくなったら終了
disableLottieView() // listener解除など終了処理
return
}
buildAndPlayLottieView(lottieUrl)
}
private fun buildAndPlayLottieView(url: String) {
LottieCompositionFactory.fromUrl(context, url)
.addFailureListener {
playNextLottie() // リンク切れなどのときは何もせず次へ
}
.addListener {
lottieView.setComposition(it)
lottieView.addAnimatorListener(animationListener)
lottieView.playAnimation()
}
}
その後、これらの処理はcom.airbnb.lottie.LottieAnimationView
を継承したCustomViewをつくって突っ込みました。
動的に取得したLottieを画面の横幅目一杯に表示する
match_parent
して伸びるのはアニメーションの背景だけだったので、scaleを設定してみました。
LottieCompositionFactory.fromUrl(this, url)
.addListener { lotteCompotition ->
lottieView.scale = lottieView.measuredWidth.toFloat() / lotteCompotition.bounds.width()
}
アスペクト比が固定なのであれば、 app:layout_constraintDimensionRatio
で縦横比を指定して match_constraint
でもいけてました。
scaleTypeも使えますが、 centerCrop
と centerInside
しか使えないので注意です。
こまった!
おちた!
json内でフォントの指定がされており、そのフォントが見つからなかったときに落ちました。次のエラーが出ました。
Font asset not found fonts/BlackHanSans.ttf
特に文字は使っていなかったので、フォント指定のないjsonを作り直してもらいました。
でない!
ViewよりLottieが大きいと表示されませんでした。scaleで調整したりしました。
おもい!
古い端末だともっさりすることがあります。json側の作り方の方は詳しくないので、軽く作る良い方法があったら知りたいです。
おわりに
アニメーションはうまく使うとわかりやすいし楽しいです。参考になる記事などをいくつか載せておきます。良いLottieライフを!
-
サクッとLottieの概要がわかる記事
- lottieが大変よろしいものだった - アナログ金木犀 https://motida-japan.hatenablog.com/entry/2017/02/19/145537
-
jsonの作り方の話
- デザイナーとエンジニアの距離をより近づける Lottie 利用術 - DroidKaigi 2019
https://droidkaigi.jp/2019/timetable/70876
- デザイナーとエンジニアの距離をより近づける Lottie 利用術 - DroidKaigi 2019
-
導入事例などの話
- 実践 Lottie - DroidKaigi 2019 https://droidkaigi.jp/2019/timetable/69036