Fragmentによる画面遷移でハマった

# やりたいこと
Fragmentで作られた画面A,B,Cがあり、A->B->Cと遷移し、 
Cでバックキーをタップした際は、Bを飛ばしてAに戻りたい

# レイアウト
## ベースのレイアウト
```xml
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
```
## 画面Aのレイアウト
```kotlin
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#00000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/button"
        android:text="Fragment A Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>
```
![device-2017-11-16-141547.png](https://qiita-image-store.s3.amazonaws.com/0/141937/8bcdb608-b62e-5c81-0c15-1f083625921f.png)

## 画面Bのレイアウト
```kotlin
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#00000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/button"
        android:text="Fragment B Button"
        android:layout_gravity="right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>
```
![device-2017-11-16-141554.png](https://qiita-image-store.s3.amazonaws.com/0/141937/dbf74885-af5e-5aee-ca1b-4c1b034e1e82.png)

## 画面Cのレイアウト
```kotlin
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00000000">

    <Button
        android:id="@+id/button"
        android:text="Fragment C Button"
        android:layout_gravity="bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>
```
![device-2017-11-16-141600.png](https://qiita-image-store.s3.amazonaws.com/0/141937/f1d46380-07a2-0cc2-28ae-2f1ca807982a.png)

# 画面遷移処理
## 画面Aの追加処理
```kotlin
    val transaction = supportFragmentManager.beginTransaction()
    transaction.add(R.id.container, FragmentA.newInstance())
    transaction.commit()
```
## 画面Bへの遷移処理
```kotlin
    val transaction = fragmentManager.beginTransaction()
    transaction.replace(R.id.container, FragmentB.newInstance())
    transaction.addToBackStack(null)
    transaction.commit()
```
## 画面Cへの遷移処理
```kotlin
    val transaction = fragmentManager.beginTransaction()
    transaction.replace(R.id.container, FragmentC.newInstance())
    // Bを飛ばしてAに戻りたいのでbackStack追加しない
    //transaction.addToBackStack(null) 
    transaction.commit()
```

上記実装で画面A->B->Cと遷移し、バックキーを押すと、なぜか画面Aと画面Cが両方表示されてしまう。
![device-2017-11-16-141606.png](https://qiita-image-store.s3.amazonaws.com/0/141937/6e277bc6-f151-faaf-6010-491c5c8dcff1.png)

# 原因
http://extra-vision.blogspot.jp/2016/02/android-fragment-transaction-back-stack.html
上記リンクが参考になった。
addToBackStackはfragmentをスタックに積む命令ではなく、fragment操作のトランザクションを記録する命令であり、バックキータップ時の処理はスタックに積んだfragmentをpopしているわけではなく、記録されたトランザクションの逆の操作を行っている、ということである。
今回のケースでは、

>add(fragment A)　トランザクション記録しない
>replace(fragment B) トランザクション記録する
>replace(fragment C) トランザクション記録しない

となっており、replaceは内部的にはremoveとaddを連続で行うのと同じことなので、展開すると、

>add(fragment A)　トランザクション記録しない
>remove(fragment A), add(fragment B) トランザクション記録する
>remove(fragment B), add(fragment C) トランザクション記録しない

となる。この状態でバックキーをタップすると、記録されたトランザクションの逆の操作を行うため、
> remove(fragment A), add(fragment B) トランザクション記録する

の逆の操作である、

> remove(fragment B), add(fragment A)

の操作が実行され、fragmentAが最前面に表示され、fragmentCは残るということになる。

# 解決策
画面Cでバックキーをタップした際の処理をフックし、自身を殺した上でpopBackStackする

```kotlin
    fragmentManager.beginTransaction().remove(this).commit()
    fragmentManager.popBackStack()
```
もしくは、addToBackStack時にタグを指定しておき、タグ指定でpopBackStackする。

```kotlin
    transaction.replace(R.id.container, FragmentB.newInstance())
    transaction.addToBackStack("fragmentA")
....
....
    fragmentManager.popBackStack("fragmentA", FragmentManager.POP_BACK_STACK_INCLUSIVE)
```