［Android］Navigation でどこまで戻るか制御する (Pop To と Inclusive)

# はじめに

`Navigation` ではどこまで戻るかを`Pop To` と `Inclusive` にて制御できます。
今回は実際にサンプルアプリを作成して、どこまで戻るか制御してみたいと思います。

| 用語 |　説明 | 
| ---- | ---- |
| Pop To | `Back`ボタンを押したとき、どこの画面まで戻るか指定できる |
| Inclusive | `Pop Up`で指定した画面の1つ前に戻るようにする |

# 準備

## `Navigation`をセットアップする

`Navigation` を利用したアプリケーションを作成するための下準備をします。
次の手順で必要なライブラリやクラス、レイアウトを生成していきます。

```groovy:build.gradle(app)
dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
```

## `Fragment` を作成する

`Navigation` を使った遷移で利用する `Fragment` を作成していきます。
次の手順で `OneFragment`, `TwoFragment`, `ThreeFragment` の 3つを作成します。

1. `Package Name` を右クリックし、`New → Fragment → Fragment(Blank)` を選択する
2. `Fragment Name` に `任意の名称` を入力し `OK` を押す
3. `Fragment` にいらない記述が書かれているので消す
4. `Layout` に `Fragment` に遷移させるための `Button` を追加する
5. `Layout` に `Fragment` を遷移させたときのパラメータを表示される`TextView`を追加する


```Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/61c22980-cfc0-4a26-9f37-f7130c175024.png)


```layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OneFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="50dp"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="bottom"
        android:text="Next" />

</FrameLayout>
```
# 実装

次の手順で実装し、`Pop To` と `Inclusive` の動きを確認して見たいと思います。

| No | 名称 |
| ------- | ------- |
| 1 | `Navigation`で画面遷移させる |
| 2 | Pop To を利用して指定した画面まで戻る  |
| 3 | Inclusive で Pop To で指定した画面の1つ前に戻る |

## Step1 `Navigation`で画面遷移させる

`Safe Args`でパラメータを引き渡すため基本となる画面遷移を実装していきます。

### Nav Graph の作成する

`nav_graph.xml`を作成し、次の内容の`Navigation Editor`で記述します。
`Nav Graph`の作成の詳しい方法は[こちら](https://qiita.com/kaleidot725/items/2fa54afa90c6c0d51c51)に書いてあるので参考にしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/63f4b068-1e42-4d83-4877-343391630578.png)


```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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/oneFragment">

    <fragment
        android:id="@+id/oneFragment"
        android:name="c.kaleidot725.safeargssample.OneFragment"
        android:label="fragment_one"
        tools:layout="@layout/fragment_one" >
        <action
            android:id="@+id/action_oneFragment_to_twoFragment"
            app:destination="@id/twoFragment" />
    </fragment>
    <fragment
        android:id="@+id/twoFragment"
        android:name="c.kaleidot725.safeargssample.TwoFragment"
        android:label="fragment_two"
        tools:layout="@layout/fragment_two" >
        <action
            android:id="@+id/action_twoFragment_to_threeFragment"
            app:destination="@id/threeFragment" />
    </fragment>
    <fragment
        android:id="@+id/threeFragment"
        android:name="c.kaleidot725.safeargssample.ThreeFragment"
        android:label="fragment_three"
        tools:layout="@layout/fragment_three" />
</navigation>
```

### `Nav Host`を作成する

`Main Activity`に`Fragment`を表示するための`Nav Host`を作成します。

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</FrameLayout>
```

### `Nav Controller` を取得して画面を遷移させる

`OneFragment`, `TwoFragment`, `ThreeFragment`で
`Nav Controller`を取得して、`Button`で画面遷移できるようにします。

```OneFragment.kt
class OneFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_one, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val b = view.findViewById<Button>(R.id.next_button)
        b.setOnClickListener {
            this.findNavController().navigate(R.id.action_oneFragment_to_twoFragment)
        }
    }
}
```

いまのところ、こんな感じで動作します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/7f7955ce-a3a7-b8c2-b2bd-3b2b8ba2dc6e.png)

## Step2 Pop To を利用して指定した画面まで戻る

`Navigation` の `Pop To` を利用するとどこ画面まで戻ることか指定できます。
通常は `OneFragment` → `TwoFragment` → `ThreeFragment`と遷移し `Back`ボタンを押すと `TwoFragment`に戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ba2caead-a780-bf49-2d50-e63087e1f923.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/256baad4-d1a4-afaa-a600-1e42e70da085.png)


`TwoFragment`→`ThreeFragment`の遷移に`Pop To`を`OneFramgnet`に設定してみます。
`OneFragment` → `TwoFragment` → `ThreeFragment`と遷移し`Back`ボタンを押すと 
`OneFragment`に戻るようになります。というように`Pop To` で指定したところまで戻ってくれるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/296c16e5-294d-540c-5f92-020abb8834c5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/7c615001-0bc3-f060-271a-2dd1684f3749.png)


## Step3 Inclusive で Pop To で指定した画面の1つ前に戻る
 
`Navigation` の `Inclusive` を利用すると、`Pop To`で指定した画面の1つに戻るようになります。
通常は `OneFragment` → `TwoFragment` → `ThreeFragment`と遷移し `Back`ボタンを押すと `TwoFragment`に戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/041c2c7b-57ba-3e9e-0cdd-cec3359f8a5f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/26aca817-29b8-daad-015e-7c6b7094659f.png)


`TwoFragment`→`ThreeFragment`の遷移に`Pop To`を`OneFramgnet`にし`Inclusive`にチェックを入れます。
`OneFragment` → `TwoFragment` → `ThreeFragment`と遷移し`Back`ボタンを押すと
`OneFragment`の1つ前の画面に戻るのでアプリケーションが終了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/ff316518-01f0-33f1-8a78-df23a4f4443a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79387/d9b9ad3c-7ba8-8e32-e45e-3d2922dc9dda.png)

`Inclusive` ですが `Pop To`を指定しない場合だと機能しませんので注意が必要です。

# おわりに

`Pop To` と `Inclusive`が詳しく解説されているところが、
少ないので理解しづらいかと思いますがわかれば簡単ですね。

# 参考記事
- [Navigationの戻る制御](https://medium.com/@star_zero/navigationの戻る制御-c469a5225e97)  
  - すごくわかりやすいです、本記事はこの記事の補足的な立ち位置で作成しました。