目的

kotlin言語でActivityからFragmentに遷移したり、
逆にFragmentからActivityに戻ったりする挙動のメモ


環境

  • Android Studio 3.5.2
  • kotlin 1.3.50


参照サイト

Qiita 【Android】Androidの基本を覚えた後に覚えたいこと~はじめてのFragment②~
それは叩いても直らない Android Fragmentのトランザクションを指定の位置まで戻す方法


メモの前に

Android初心者なので間違っている可能性があります。

  • とにかくFragmentが分かりづらい
    iOSで言うところのchildViewControllerの位置付けだと思うのですが。
    私の場合、iOSでもchildViewControllerなんてあまり使わないんですよね。
    使っている案件の運用を任されたことありましたが、すごく改修しづらかった思い出が。
    画面サイズとか、親のVCは何だとか。。
    AndroidはそのFragmentを多様するとかね。

  • 画面全体を遷移したいと言う場合、
    ActivityとFragmentどちらを使うべきかの明確な違いも分かりません。
    Fragmentの方が挙動が早いとのことですが。

  • Fragmentに遷移したとき、画面が透過してしまう。背景色を設定して透けなくしましたが、私の実装が間違っているのではとヒヤヒヤします。

    意外とガッツリ紹介してるサイトが見当たらず


実装メモ

準備としては
下図の左から順に
ActivityFragment1Fragment2Fragment3Fragment4と用意しました。


nextボタンではActivityからFragmentの番号順に進んでいきます。
prevボタンでは

  • Fragment1はActivityに戻る。
  • Fragment2はFragment1に戻る。
  • Fragment3はActivityに戻る。
  • Fragment4はFragment1に戻る。

と特殊なことをしています。


kotlinファイルやxmlレイアウトについて

最初のActivityは簡潔に以下のようにしています。

activity
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button = findViewById<Button>(R.id.buttonmain)
        button.setOnClickListener {
            val transaction = supportFragmentManager.beginTransaction()
            transaction.add(R.id.fragment_layout, FirstFragment.createInstance())
            transaction.addToBackStack(null)
            transaction.commit()
        }
    }
}


そして、Fragmentは大体以下にしていますが、
戻るボタンbuttonprevのタップ時の処理はFragmentによって変更しています。

fragmentたち
class FirstFragment : Fragment() {

    companion object {
        fun createInstance(): FirstFragment {
            return FirstFragment()
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment

        return inflater.inflate(R.layout.fragment_first, container, false)
    }

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

        val buttonnext = view.findViewById<Button>(R.id.buttonfirstnext)
        buttonnext.setOnClickListener {
            val transaction = activity?.supportFragmentManager?.beginTransaction()
            transaction?.add(R.id.fragment_layout, SecondFragment.createInstance())
            transaction?.addToBackStack(null)
            transaction?.commit()
        }

        val buttonprev = view.findViewById<Button>(R.id.buttonfirstprev)
        buttonprev.setOnClickListener {
            activity?.supportFragmentManager?.popBackStack()
        }
    }
}

xmlレイアウトはどれも似たようなものにしています。
idはもちろんファイル毎に変更しました。
xmlレイアウト
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFF">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="first"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/buttonfirstprev"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="92dp"
        android:text="prev"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
    <Button
        android:id="@+id/buttonfirstnext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:text="next"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ボタン処理について

ボタンをタップした時の処理について書きます。
まずは次の画面に進む時です。
どれも一緒なので、これも例として1つだけ載せます。
注意点としては、transaction?.addToBackStack(null) を忘れないことでしょうか。
これを省くことで、FragmentのBackStackに記録しないという挙動を行うようですが。
なんか挙動があやしいなぁと自分なりに思いましたし、今回の検証で必要なものとします。

ボタンタップで次の画面に遷移
val buttonnext = view.findViewById<Button>(R.id.buttonfirstnext)
buttonnext.setOnClickListener {
    val transaction = activity?.supportFragmentManager?.beginTransaction()
    transaction?.add(R.id.fragment_layout, SecondFragment.createInstance())
    transaction?.addToBackStack(null)
    transaction?.commit()
}


次に前の画面に戻る時の処理です。
まずは、1つ前の画面に戻るとき です。
今回のパターンでは

  • Fragment1はActivityに戻る。
  • Fragment2はFragment1に戻る。

時に使いました。

ボタンタップで一つ前の画面に戻る
val buttonprev = view.findViewById<Button>(R.id.buttonfirstprev)
buttonprev.setOnClickListener {
    activity?.supportFragmentManager?.popBackStack()
}


そして、最初のActivityに戻る時 です。
今回のパターンでは

  • Fragment3はActivityに戻る。

時に使いました。
記録しているFragmentをすべて削除するようなイメージです。

xmlレイアウト
val buttonprev = view.findViewById<Button>(R.id.buttonthirdprev)
buttonprev.setOnClickListener {
    val entry = activity?.supportFragmentManager?.getBackStackEntryAt(0)
    entry?.id?.let { _entry ->
        activity?.supportFragmentManager?.popBackStack(_entry, FragmentManager.POP_BACK_STACK_INCLUSIVE)
    }
}


最後に、最初のFragmentに戻る時 の処理です。
今回のパターンでは

  • Fragment4はFragment1に戻る。

時に使いました。

xmlレイアウト
val buttonprev = view.findViewById<Button>(R.id.buttonfourth)
buttonprev.setOnClickListener {
    val entry = activity?.supportFragmentManager?.getBackStackEntryAt(0)
    entry?.id?.let { _entry ->
        activity?.supportFragmentManager?.popBackStack(_entry, 0)
    }
}



ちなみに

上で使っている。getBackStackEntryAt(0) ですが、カッコの数字によって戻れるFragmentを変更できます。
Fragment4でgetBackStackEntryAt(1)とするとFragment2に遷移できます。

もう一つ、
FragmentManager.POP_BACK_STACK_INCLUSIVE ですが、これはgetBackStackEntryAtで指定さらたFragmentを削除するみたいな挙動を取るようです。

Fragment4のとき以下のようにしても、Fragment1に戻ります。

POP_BACK_STACK_INCLUSIVEの説明
val entry = activity?.supportFragmentManager?.getBackStackEntryAt(1)
entry?.id?.let { _entry ->
    activity?.supportFragmentManager?.popBackStack(_entry, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

数字指定の時との違いは調べきれませんでした。
この辺りの仕様も、うまくいっているのかの不安感を増大させています。

設定方法がいくつもあるというのは、初心者には辛い

メモは以上です。