[Android] もっと！Lottie

【移行のお知らせ】[プライバシー上の懸念](https://blog.qiita.com/about-user-page-renewal-20200326/)により、Qiitaアカウントの削除を検討しています。
各投稿は個人blog( https://gateau.hatenablog.com/archive/category/Qiita )に移行しました。
今後も参照される方は、ブックマークなど移行をお願い致します。

---

Lottie( http://airbnb.io/lottie/ )はAirbnb社が開発・公開しているアニメーションライブラリです。
アニメーションのjsonさえあれば、ImageViewと似た指定で簡単に扱えます。[サンプルアプリ](https://play.google.com/store/apps/details?id=com.airbnb.lottie) を入れてみると挙動がわかりやすいです。
jsonの作り方は他のドキュメントに任せて、Androidの実装側で色々やってみた例を紹介します。

## きほん

### アプリ内のassetsなどに置いたjsonを読み込む

```kotlin
 <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で指定する
```kotlin
 <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だけでは出来なくて、コードを書くことになります。

```kotlin
lottieView.setAnimationFromUrl(url)
```
この指定で表示はされます。しかし、URLが存在しなかったりロード出来なかったときに IllegalStateException が投げられてしまいます。
たぶんこの部分 https://github.com/airbnb/lottie-android/blob/master/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java#L66

```java
private final LottieListener<Throwable> failureListener = new LottieListener<Throwable>() {
    @Override public void onResult(Throwable throwable) {
        throw new IllegalStateException("Unable to parse composition", throwable);
    }
};
```
落ちないように次のようにしました。

```kotlin
LottieCompositionFactory.fromUrl(this, url)
        .addFailureListener {
            // 読み込み失敗時の処理
        }
        .addListener {
            lottieView.setComposition(it)
        }
```
コードで動的に指定するときは `lottieView.playAnimation()` を忘れるとアニメーションが開始しないので注意です。

### アニメーションが終わったら消す
AnimatorListenerかAnimatorListenerAdapterのListenerをセットし、アニメーションの開始や終了時の処理を記述できます。

```kotlin
lottieView.addAnimatorListener(object : AnimatorListenerAdapter() {
    override fun onAnimationEnd(animation: Animator?) {
        lottieView.visibility = View.GONE
    }
})
```

### 複数のアニメーションを連続して表示する
サーバから1つずつ表示したいアニメーションのURLを取得し、それをQueueに詰めてみることにしました。

```kotlin
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を設定してみました。

```kotlin
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 https://droidkaigi.jp/2019/timetable/69036