Android Support Library v4を使っていますが、FragmentTabHostのタブのひとつにSupportMapFragmentを継承したFragmentを配置、その地図上のマーカーから同じタブ内に詳細情報表示用のFragmentを表示させるようにしていました。
詳細情報表示用のFragmentを開いたまま、他のタブに行く際に、Fragmentスタックを全部クリアしてから移動するのですが、そうするとこのタブに戻ってきたときにSupportMapFragmentがまったく反応しなくなってしまいます。
対策としては、一度detach/removeしておいて、再度SupportMapFragmentのインスタンス化をして、Transactionにaddという流れでした。
@Override
public void onTabChanged(String tabId) {
FragmentManager fm = getSupportFragmentManager();
// 移動前に既存のFragmentスタックをクリアする
for (int i = 0; i < fm.getBackStackEntryCount(); ++i) {
fm.popBackStack();
}
// 地図タブから移動する場合は、再アクティブ化のために一度Fragmentをデタッチする
if (lastTabId != null && lastTabId.equals("map_section")) {
MapSectionFragment fragment = (MapSectionFragment) fm
.findFragmentByTag(lastTabId);
if (fragment != null) {
FragmentTransaction ft = fm.beginTransaction();
ft.detach(fragment);
ft.remove(fragment);
ft.commit();
}
}
// 地図タブを開くときは、removeされていれば再インスタンス化する
if (tabId.equals("map_section")) {
// FragmentTabHost.onTabChangedでcommitされたスタックを先に反映させないとfindできない
fm.executePendingTransactions();
MapSectionFragment fragment = (MapSectionFragment) fm
.findFragmentByTag(tabId);
FragmentTransaction ft = fm.beginTransaction();
if (fragment == null) {
// インスタンス化
fragment = (MapSectionFragment) Fragment.instantiate(this,
MapSectionFragment.class.getName(), null);
// Fragmentを追加
ft.add(R.id.content, fragment, "map_section");
}
ft.commit();
}
// 最後に開いたタブのタグを保持する
lastTabId = tabId;
}
今回キモだったのはexecutePendingTransactions()です。
v4/java/android/support/v4/app/FragmentTabHost.java – platform/frameworks/support – Git at Google
FragmentTabHostのソースを見ると、doTabChangedメソッドで同様の処理を実行してFragmentのインスタンス化、追加を実行、コミットしているにも関わらず、findFragmentByTagがnullを返す現象が発生しました。
これはコミットが実際にはすぐには実行されず、メインスレッドにスケジューリングされるためだそうです。
android – Can’t find fragment by tag – Stack Overflow
ドキュメントに書いてありました。
FragmentTransaction | Android Developers
executePendingTransactionsを実行しておけば、地図が二重に表示されるなどの現象はなくなります。