それっぽいローディングアニメーションをAnimatedVectorDrawableで作る

onDrawで描画しようかなーと思っていたけど、AnimatedVectorDrawableでいけるんじゃないかと気づいたので、お試し。

https://github.com/mayahiro/AnimatedVectorDrawableSample

# 出来あがり

![dots_wave.gif](https://qiita-image-store.s3.amazonaws.com/0/69858/7f5c989a-6191-d09f-18c4-25e8015048a8.gif)

# お悩みポイント

## [Shape Shifter](https://shapeshifter.design/)に大量の丸をいい感じに追加したい

- 取っ掛かりとしてShape Shifterを使ったのだけど、Sketchからエクスポートした丸を複数インポートする方法がよくわからなかった
- 結果として、以下の手順を踏むことでうまくインポートできた
    1. Sketchでアートボードを作って、必要な数の丸を並べる
    2. アートボードをSVGとして、エクスポート
        - このSVGをShape Shifterにインポートしても、丸が個別に認識されなかった…
    3. エクスポートしたSVGをAndroidStudioに取り込んで、VectorDrawableに変換
    4. VectorDrawableをShape Shifterにインポート
    5. 丸が個別に認識されるので、いい感じにアニメーションを当てれる

## どうやってループさせるの？

- repeatModeみたいな設定することが出来なさげ
    - 個別のobjectAnimatorには設定できるかもしれないけど（未検証）、全体のアニメーションを一纏まりとしてループしてほしい
- 実直にアニメーションの終了タイミングを捕まえて、再度startを実行した
    - 参考: https://medium.com/google-developers/re-animation-7869722af206

```kotlin:こんな感じ
val imageView = findViewById<AppCompatImageView>(R.id.image_view)
val drawable = imageView.drawable as AnimatedVectorDrawableCompat
drawable.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
    override fun onAnimationEnd(drawable: Drawable?) {
        imageView.post { (drawable as AnimatedVectorDrawableCompat).start() }
    }
})
drawable.start()
```
# ちなみにXMLファイルはどうなったか

- 長くなった
    - アニメーションの設定は全てをShape Shifter上で行った訳ではなくて、2個分設定してエクスポートしたものをコピペして量産した
    - そこでタイミングなども調整
    - こう見ると丸のpathにも当然規則性がある訳で、無理にShape Shifterに大量の丸をインポートしなくてもコピペでいけたなと…

```xml:avd_dots_anim.xml
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector
            android:name="dots"
            android:width="64dp"
            android:height="12dp"
            android:viewportHeight="12"
            android:viewportWidth="64">
            <group android:name="dot_1">
                <path
                    android:name="dot_path_1"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 4 6 M 2 6 C 2 4.895 2.895 4 4 4 C 5.105 4 6 4.895 6 6 C 6 7.105 5.105 8 4 8 C 2.895 8 2 7.105 2 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_2">
                <path
                    android:name="dot_path_2"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 12 6 M 10 6 C 10 4.895 10.895 4 12 4 C 13.105 4 14 4.895 14 6 C 14 7.105 13.105 8 12 8 C 10.895 8 10 7.105 10 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_3">
                <path
                    android:name="dot_path_3"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 20 6 M 18 6 C 18 4.895 18.895 4 20 4 C 21.105 4 22 4.895 22 6 C 22 7.105 21.105 8 20 8 C 18.895 8 18 7.105 18 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_4">
                <path
                    android:name="dot_path_4"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 28 6 M 26 6 C 26 4.895 26.895 4 28 4 C 29.105 4 30 4.895 30 6 C 30 7.105 29.105 8 28 8 C 26.895 8 26 7.105 26 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_5">
                <path
                    android:name="dot_path_5"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 36 6 M 34 6 C 34 4.895 34.895 4 36 4 C 37.105 4 38 4.895 38 6 C 38 7.105 37.105 8 36 8 C 34.895 8 34 7.105 34 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_6">
                <path
                    android:name="dot_path_6"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 44 6 M 42 6 C 42 4.895 42.895 4 44 4 C 45.105 4 46 4.895 46 6 C 46 7.105 45.105 8 44 8 C 42.895 8 42 7.105 42 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_7">
                <path
                    android:name="dot_path_7"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 52 6 M 50 6 C 50 4.895 50.895 4 52 4 C 53.105 4 54 4.895 54 6 C 54 7.105 53.105 8 52 8 C 50.895 8 50 7.105 50 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
            <group android:name="dot_8">
                <path
                    android:name="dot_path_8"
                    android:fillColor="#D8D8D8"
                    android:pathData="M 60 6 M 58 6 C 58 4.895 58.895 4 60 4 C 61.105 4 62 4.895 62 6 C 62 7.105 61.105 8 60 8 C 58.895 8 58 7.105 58 6"
                    android:strokeColor="#00000000"
                    android:strokeWidth="1" />
            </group>
        </vector>
    </aapt:attr>
    <target android:name="dot_1">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="100"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="350"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="850"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_2">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="200"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="450"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="950"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_3">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="300"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="550"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1050"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_4">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="400"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="650"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1150"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_5">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="500"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="750"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1250"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_6">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="600"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="850"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1350"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_7">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="700"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="950"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1450"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
    <target android:name="dot_8">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="800"
                    android:valueFrom="0"
                    android:valueTo="4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="500"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1050"
                    android:valueFrom="4"
                    android:valueTo="-4"
                    android:valueType="floatType" />
                <objectAnimator
                    android:duration="250"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="translateY"
                    android:startOffset="1550"
                    android:valueFrom="-4"
                    android:valueTo="0"
                    android:valueType="floatType" />
            </set>
        </aapt:attr>
    </target>
</animated-vector>
```