もっと!Lottie

※本記事はQiitaに投稿していた記事のexportです(元投稿日: 2019/04/05)

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も使えますが、 centerCropcenterInside しか使えないので注意です。

こまった!

おちた!

json内でフォントの指定がされており、そのフォントが見つからなかったときに落ちました。次のエラーが出ました。

Font asset not found fonts/BlackHanSans.ttf

特に文字は使っていなかったので、フォント指定のないjsonを作り直してもらいました。

でない!

ViewよりLottieが大きいと表示されませんでした。scaleで調整したりしました。

おもい!

古い端末だともっさりすることがあります。json側の作り方の方は詳しくないので、軽く作る良い方法があったら知りたいです。

おわりに

アニメーションはうまく使うとわかりやすいし楽しいです。参考になる記事などをいくつか載せておきます。良いLottieライフを!