Jetpack Navigation で Material 3 に沿った画面遷移アニメーションを設定する

# はじめに

Android アプリにおける画面遷移は主に

- [Activity による画面遷移](https://developer.android.com/training/basics/firstapp/starting-activity?hl=ja#BuildIntent)
- [Jetpack Navigation](https://developer.android.com/guide/navigation) を用いた Fragment による画面遷移
- [Compose を使用したナビゲーション](https://developer.android.com/jetpack/compose/navigation)

がありますが、この記事では Jetpack Navigation による画面遷移のアニメーションについて述べます。Jetpack Naviation は Compose 登場前から開発しているアプリでは、現在でも使われているケースが多いと思います。

## Jetpack Navigation による画面遷移は設定しなければアニメーションなし

[Jetpack Navigation では XML で画面遷移を定義します](https://developer.android.com/guide/navigation/navigation-getting-started#add-destinations)が、それだけでは画面遷移がカットインになっていしまい不自然です。

```nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@+id/screen1">
    <fragment
        android:id="@+id/screen1"
        android:name="com.tfandkusu.ate.feature.jnta.Screen1Fragment">
        <action
            android:id="@+id/actionScreen2"
            app:destination="@+id/screen2" />
    </fragment>
    <fragment
        android:id="@+id/screen2"
        android:name="com.tfandkusu.ate.feature.jnta.Screen2Fragment" />
</navigation>
```

```Screen1Fragment.kt
// Screen1Fragment から Screen2Fragment に遷移する
findNavController().navigate(
    Screen1FragmentDirections.actionScreen2(),
)
```

![cutin.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/e9e85037-fb0c-76a0-d2f8-4499a6ee9e2f.gif)


## ナビゲーション グラフ XML にアニメーションを設定する

ナビゲーション グラフ XML の [action タグにアニメーションを設定する](https://developer.android.com/guide/navigation/navigation-animate-transitions)ことができます。

```nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@+id/screen1">
    <fragment
        android:id="@+id/screen1"
        android:name="com.tfandkusu.ate.feature.jnta.Screen1Fragment">
        <!-- アニメーション追加 -->
        <action
            android:id="@+id/actionScreen2"
            app:destination="@+id/screen2"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
    </fragment>
    <fragment
        android:id="@+id/screen2"
        android:name="com.tfandkusu.ate.feature.jnta.Screen2Fragment" />
</navigation>
```

```slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="100%p"
        android:toXDelta="0" />
</set>
```

```slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:toXDelta="-100%p" />
</set>
```

```slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="-100%p"
        android:toXDelta="0" />
</set>
```

```slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:toXDelta="100%p" />
</set>
```

![slide.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/e64312e9-1dc1-54fe-6052-36722eee6a5f.gif)

今回は左右にスライドする画面遷移のアニメーションを設定しました。

## 疑問

ところで、このアニメーションは Android として自然、言い換えると Material Design に沿ったアニメーションなのでしょうか。次の節からは Material Design の公式サイト https://m3.material.io/ を読んで、それに沿ったアニメーションを設定します。

# Material Design に沿って画面遷移アニメーションを設定する

Material Design の公式サイト https://m3.material.io/ の [Styles → Motion → Transitions](https://m3.material.io/styles/motion/transitions/transition-patterns) に画面遷移アニメーションのガイドラインがありました。

最初に [Container transform](https://m3.material.io/styles/motion/transitions/transition-patterns#b67cba74-6240-4663-a423-d537b6d21187) という、共通要素の View を使って自然な画面遷移をする別名 [Shared element transition](https://developer.android.com/develop/ui/views/animations/transitions/start-activity#start-with-element) に言及していますが、今回はその次の [Forward and backward](https://m3.material.io/styles/motion/transitions/transition-patterns#df9c7d76-1454-47f3-ad1c-268a31f58bad) を設定します。

[Forward and backward](https://m3.material.io/styles/motion/transitions/transition-patterns#df9c7d76-1454-47f3-ad1c-268a31f58bad) には、アニメーション動画とともに *Android implementation* というリンクがあるので、そこに飛びます。

[Shared axis](https://github.com/material-components/material-components-android/blob/master/docs/theming/Motion.md#shared-axis) という節でアニメーションの設定方法を解説しているので、同じように設定します。

```Screen1Fragment.kt
// 画面遷移アニメーションを設定する
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
// Screen3Fragment に遷移する
findNavController().navigate(Screen1FragmentDirections.actionScreen3())
```

```Screen3Fragment.kt
class Screen3Fragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 画面遷移アニメーションを設定する
        enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
        returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
    }

    // 略
}
```

[Forward and backward](https://m3.material.io/styles/motion/transitions/transition-patterns#df9c7d76-1454-47f3-ad1c-268a31f58bad) の解説通りのアニメーションが設定できました。

![forward.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/0f7c9a61-0cb5-d20e-4ee9-c4f18b767074.gif)

# まとめ

この記事では画面遷移に [Jetpack Navigation](https://developer.android.com/guide/navigation) を使っているケースで、 [Material Design](https://m3.material.io/) で紹介されている　 [Forward and backward](https://m3.material.io/styles/motion/transitions/transition-patterns#df9c7d76-1454-47f3-ad1c-268a31f58bad) アニメーションの設定方法を解説しました。

例えば、iOS の画面遷移に寄せる形で Android 側は独自にアニメーションを設定していたが、やっぱり Android アプリは Android らしさを追求したい場合は、今回紹介した Material Design のサイトを参考にして Android らしい画面遷移アニメーションを設定することができます。

iOSの画面遷移アニメーションの例

![ios_push.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/0dfd8850-7971-6c3d-4baf-162aafdeef2b.gif)