2017年7月27日木曜日

複数積んだFragmentBackStackを一度にクリアする方法

Androidアプリを作るとき昔はActivityを複数作ってIntentで画面切替をしていた。
今は一つのActivityの中にFragmentを突っ込んでFragmentを切り替えることで画面切替をするようにしている。(一般的にもこの方法が今はメジャーなはず)

FragmentA、FragmentB、FragmentCと3つのFragmentがあったとして、
Fragmentでの画面切替は普通こんな感じで行う。

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.container, FragmentA.newInstance() );
transaction.addToBackStack(null);
transaction.commit();

commit時にBackStackに積んでいるので
[A] -> [B] -> [C] と画面切替をした後バックキーを押す度に

[C] -> [B]

[B] -> [A]

と切り替わる。

しかし、一度に [C] -> [A]に戻りたいときどうすれば良いかを分らなかった。

調べたところ、BackStackはFragmentのインスタンスを保存しているのではなく、transactionで行ったことを記録しているだけらしく、popBackStack()が実行されると記録されたことと逆のことが実行される。

例えば、replace(FragmentB)(中身はremove(FragmentA)してadd(FragmentB))が記録されていた場合
その逆のremove(FragmentB)してadd(FragmentA)が実行されるらしい。


知らなかったーーーー。

それならば、話は簡単で[C] -> [A]に一度のバックキーで戻るにはFragmentBackStackの数が変わったことをイベントトリガとして、BackStackの数が0に成るまでpopBackStackを呼んでやれば良い。

FragmentBackStackの数の変更はFragmentManager.OnBackStackChangedListnerを実現することで検知できる。

以下サンプルコード

public class MainActivity implements FragmentManager.OnBackStackChangedListener{
    private int fragmentBackStackCnt = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().add(R.id.container, FragmentA.newInstance()).commit();
        fragmentManager.addOnBackStackChangedListener(this);
    }

    @Override
    public void onBackStackChanged() {
        FragmentManager fragmentManager = getFragmentManager();
        int count = fragmentManager.getBackStackEntryCount();

        if (fragmentBackStackCnt > count && count != 0) {
            // backStackが0になる(検索画面になるまで)fragmentをpopする
            fragmentManager.popBackStack();
        }
        fragmentBackStackCnt = count;
    }

}

参考ページ:
Android Fragment トランザクション - バックスタックの落とし穴