MotionLayoutのKeyCycleを使ってアニメーションを実装してみた

この記事は、[and factory Advent Calendar 2020](https://qiita.com/advent-calendar/2020/andfactory)の**23日目**の記事です。
昨日は[@cpp0302](https://qiita.com/cpp0302)さんの「[GoのDIライブラリ google/wire でモックを使う場合のベストプラクティス](https://qiita.com/cpp0302/items/3c5254b840df6af24c10)」でした！



## 試したこと
- クリックすると画像が揺れるアニメーションをMotionLayoutのKeyCycleを使って実装してみました
  - 参考にしたアニメーション

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36517/9f4808b0-000f-efcd-1933-5688751cff6f.gif" width="320"/> 

## 環境
- Android Studio 4.0.1
- constraintlayout:2.1.0-alpha1


## MotionLayoutとは
- アニメーションを構築するためのConstraintLayoutのサブクラス。2.0で追加されました。
- 宣言的にアニメーションを実装することができる
    - 複雑な遷移をXMLで記述できる
- ユーザーが操作するUI要素をアニメーション化するときにMotionLayoutを使用する


## KeyCycleとは
- アニメーション中のポストレイアウトプロパティの位置に関する振動を制御する :rolling_eyes:


## 実装

```xml.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/motion_key_cycle_01"
    tools:showPaths="true">

    <ImageView
        android:id="@+id/button"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/alpaca"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.motion.widget.MotionLayout>
```

```xml.motion_key_cycle_01.xml
<KeyFrameSet>
    <KeyCycle
        android:rotationX="20"
        app:framePosition="10"
        app:motionTarget="@+id/button"
        app:waveOffset="0"
        app:wavePeriod="1"
        app:waveShape="sin" />
    <KeyCycle
        android:rotationY="20"
        app:framePosition="30"
        app:motionTarget="@+id/button"
        app:waveOffset="0"
        app:wavePeriod="1"
        app:waveShape="sin" />
    <KeyCycle
        android:rotationX="20"
        app:framePosition="50"
        app:motionTarget="@+id/button"
        app:waveOffset="1"
        app:wavePeriod="1"
        app:waveShape="sin" />
    <KeyCycle
        android:rotationY="20"
        app:framePosition="70"
        app:motionTarget="@+id/button"
        app:waveOffset="1"
        app:wavePeriod="1"
        app:waveShape="sin" />
</KeyFrameSet>
```

## 表示

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36517/3775808e-aac0-d70f-bcb8-c27f922d6872.gif" width="320"/> 


## 終わりに
- 少し動作がぎこちない箇所はありますが、同じようなアニメーションが再現できたのかなと思います
    - waveOffsetの効果はまだよくわかっていないところです。 
- KeyCycleを使うと揺らぎ？のあるアニメーションをXMLで実装できるようでした
- クリックする度に異なるアニメーションが再生されることがありました。毎回、同じアニメーションを再生する場合は、最初の状態に戻す制御が別で必要そうです
- [github](https://github.com/yiwaisako/motionlayout_sample)

## 参考ページ
- [MotionLayoutKeyCycleの調査]
(https://proandroiddev.com/exploring-motionlayout-keycycle-b990d9cef5bf)

- [Google I/O 2018で紹介されたMotionLayoutでできることとできないこと](https://medium.com/@itometeam/google-i-o-2018%E3%81%A7%E7%B4%B9%E4%BB%8B%E3%81%95%E3%82%8C%E3%81%9Fmotionlayout%E3%81%A7%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%93%E3%81%A8%E3%81%A8%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8-aefb4073e51a)

- [MotionLayoutのAPIリファレンス]
(https://developer.android.com/reference/androidx/constraintlayout/motion/widget/MotionLayout)

- [KeyCycle - Motion Tags Ep8]
(https://www.youtube.com/watch?v=qWmU6emSQ5k)