Fragment回退栈相关操作

在我的场景里,会创建多个Fragment,Fragment之间可以互相跳转,点击返回键需要一级一级往上返回。因此需要一个类似于Activity的回退栈,当然没必要做到Activity那么复杂,满足先进先出的效果即可。

添加Fragment回退栈

添加个Fragment,并将其加入回退栈,代码如下:

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
        .add(R.id.frag_container, fragment)
        .addToBackStack(null)
        .commitAllowingStateLoss();

方法addToBackStack表示将Fragment加入到回退栈中。
在返回时,Fragment出栈的逻辑如下:

FragmentManager fragmentManager = getSupportFragmentManager();
int backStackCount = fragmentManager.getBackStackEntryCount();
if (backStackCount > 0) {
    fragmentManager.popBackStack();
}

方法popBackStack将一个Fragment出栈,调用前可以先通过getBackStackEntryCount方法判断当前回退栈是否还有Fragment。

获取顶部的Fragment

我的场景中,经常要获取栈顶的Fragment进行操作,然而没有找到直接获取栈顶Fragment的方法。在最初的实现中,借用了Stack来实现我的逻辑。

private final Stack<Fragment> mFragmentStack = new Stack<>();

public void addFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
	fragmentManager.beginTransaction()
        .add(R.id.frag_container, fragment)
        .addToBackStack(null)
        .commitAllowingStateLoss();
	mFragmentStack.push(fragment);
}

public void popFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
	int backStackCount = fragmentManager.getBackStackEntryCount();
	if (backStackCount > 0) {
    	    fragmentManager.popBackStack();
            mFragmentStack.pop();
	}
}

public Fragment getTopFragment() {
    return mFragmentStack.peek();
}

然而使用Stack后发现线上版本会发生crash。mFragmentStack里的Fragment和FragmentManager中的Fragment可能不一致。主要原因是Activity状态恢复导致的,Stack数据没有saveInstanceState,Activity恢复时执行mFragmentStack.peek会导致EmptyStackException
要修复这个问题,可以将Stack数据也进行状态保存。但我仔细想过后觉得,Stack和FragmentManager的作用是一致的,加一个Stack本身是不合理的,后续代码变更,也很难保证Stack和FragmentManager回退栈数据是一致的。为此我开始想另外的解决方案。
回到我最开始的需求,我就是想获取顶部的Fragment,能不能用FragmentManager提供的接口来实现呢?阅读相关源码后,有个方法引起了我的注意:

/**
 * Return the BackStackEntry at index <var>index</var> in the back stack;
 * entries start index 0 being the bottom of the stack.
 */
@NonNull
public BackStackEntry getBackStackEntryAt(int index) {
    return mBackStack.get(index);
}

获取到的BackStackEntry有id和name属性,为此我想到一个办法:

public Fragment getTopFragment() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    FragmentManager.BackStackEntry entry = getSupportFragmentManager().getBackStackEntryAt(count - 1);
    return getSupportFragmentManager().findFragmentByTag(entry.getName());
}

该代码中,首先获取回退栈数量,然后通过getBackStackEntryAt方法,获取最后一个回退栈数据。最后通过findFragmentByTag找到顶部的Fragment。不过这里要注意的是,在添加回退栈时,需要设置tag。具体代码如下:

public void addFragment(Fragment fragment) {
    String tag = getFragmentTag();
    FragmentManager fragmentManager = getSupportFragmentManager();
	fragmentManager.beginTransaction()
        .add(R.id.frag_container, fragment, tag)
        .addToBackStack(tag)
        .commitAllowingStateLoss();
}

上面代码中,add和addToBackStack方法都指定了同一个tag,保证后续能够通过findFragmentByTag找到相应的Fragment。
方案最终验证OK。

热门相关:斗神战帝