Fragment を使った画面遷移メモ

最近 Fragment 周りの実装を追う機会があり、 Android Developers の FragmentTransaction ページは各メソッドの説明が若干分かりづらいと思ったので自分用のメモとして主要なメソッドをまとめてみることにしました。

add

名前の通り Fragment を追加するときに利用します。 特定の container(ViewGroup) に Fragment を追加する場合の他、UIを持たない Fragment を追加する際にも利用します。 DialogFragment#show()内部的には FragmentTransaction#add() を呼び出しています

注意点として、画面遷移の際に add() を利用した場合、遷移前の Fragment に重ねて表示されるため、別途remove()する必要があります。 通常は画面遷移の用途としては add() と remove() を同時に行う replace() を利用するのが良いでしょう。

remove

指定したFragmentを削除するときに使います。 削除されたFragmentはActivityとの関連付けが解除され、 onDestroyView() -> onDestroy() -> onDetach() というライフサイクルを辿ります。 対象のFragmentに対する参照を自前で持っていない場合、Fragmentインスタンスは自動的に破棄され、再表示することはできません。

ただし、 remove() 操作の大きな特徴として、同時に addToBackStack() されている場合には Activity との関連付け解除が行われず、 onDestroyView() までしか実行されません。 これにより、遷移前のFragmentのインスタンスを維持したまま画面の完全な切り替えを行うことが可能です。

replace

replace()remove()add() を同時に行うメソッドで、主に画面遷移のために利用します。 同時に addToBackStack() を呼んでいない場合、遷移元の Fragment は Activity との関連付けが解除されますが、呼んでいる場合は View だけが破棄されている状態になり、同一Fragmentを再利用可能です。

ちなみに、対象の container に Fragment が何も表示されていない状態でも replace() によって画面遷移する事は可能なので、初期表示画面への遷移も replace() で実装することは可能です。 稀に初期表示画面を add() で実装して Activity 再生成のたびに初期表示画面が追加されていくアプリがあるので、画面遷移では replace() に統一してしまっても良いかもしれません。

addToBackStack

指定したトランザクションをコミットした後で元に戻したい場合に利用します。 バックスタックに積まれたトランザクションは back ボタンを押下することで自動的に pop され、トランザクションで指定されていた動作は元に戻ります。 (ただし、自動的に pop されるのは Activity が持つ FragmentManager のバックスタックに積まれた場合のみです。 getChildFragmentManager() を利用した場合は後述します) 基本的には replace() あわせて利用することで画面遷移を戻れるようにするために利用します。

detach(), attach()

detach() を利用すると Activity に追加済みの Fragment をUIから取り外すことができます。 具体的には、 remove()addToBackStack() 付きで呼び出した場合と同じく、 onDestroyView() までライフサイクルが進んだ状態になり View が破棄されつつFragment自体は残る状態になります。 ややこしいですが、 Fragment のライフサイクルである Fragment#onAttach()Fragment#onDetach() とは関係ありません。

attach() は一度 detach() されたFragmentのUIを再度生成して表示する場合に利用します。 なかなか使いどころがないと思うかもしれませんが、 Fragment が持つ OptionsMenu やネストされた Fragmentなども View と同時に破棄/再生成されるため、タブの切替処理などで有用です。 FragmentTabHostFragmentPagerAdapterではこの仕組を利用して表示Fragmentの切り替えを行っています。

show(), hide()

Fragmentの表示状態を切り替えます。 detach(), attach() と違い、Viewの破棄や再生成が行われず、ライフサイクルも変化しません。 Fragment側のフラグで管理しているため、hide()したFragmentをremove() -> add()し直した場合でもView要素は表示されません。 OptionsMenu も同時に制御されて便利ですが、View自体は生成されたままなので画面遷移の多くをこの仕組で行う場合はメモリ消費に注意が必要です。

setPrimaryNavigationFragment()

setPrimaryNavigationFragment は v26.1.0 で追加された比較的新しいメソッドです。 このメソッドを呼び出すと、これ以降のbackボタン操作において指定したFragmentが childFragmentManager の popBackStack() を処理するようになります。

  1. fragmentA に遷移(replace)
  2. fragmentA の childFragmentManager で childFragmentB に遷移(replace & addToBackStack)
  3. fragmentA の childFragmentManager で childFragmentC に遷移(replace & addToBackStack)

という遷移を行う時、 1.replace() と同時に setPrimaryNavigationFragment(fragmentA) を呼び出しておくことで childFragmentC -> childFragmentB -> fragmentA というバックスタックの解決を backボタンだけで行うことができて非常に便利です。 特にタブ実装などで複雑なFragmentの入れ子を管理する場合に detach(), attach() と組み合わせて利用することになりそうです。

commit(), commitNow()

どちらのメソッドも設定中のトランザクションを実行します。 commit()commitNow() の違いは即時実行されるかどうかですが、 どちらの場合でも onSaveInstanceState() 以降に呼ばれた場合は IllegalStateException が発生するので非同期処理から呼び出す場合には注意が必要です。

commitAllowingStateLoss(), commitNowAllowingStateLoss()

どちらのメソッドも設定中のトランザクションを実行します。 commit(), commitNow() と違い、 onSaveInstanceState() 以降に呼ばれた場合でも IllegalStateException を発生させません。 state は Fragment のsavedInstanceState ではなく、Activity側のstateとして保存されるこのコミット自体を指していることに注意してください。 このメソッドを使って onSaveInstanceState() 以降に行った操作は Activity に保存されません。 何らかの理由でActivity自体が再生成された場合、このメソッドによって行われた操作は復元されなくなります。