使用碎片清除后退栈

279
我将我的Android应用程序移植到Honeycomb,进行了大规模重构以使用片段。在我的先前版本中,当我按下Home键时,我曾经执行ACTIVITY_CLEAR_TOP以重置后退堆栈。 现在我的应用程序只是一个包含多个片段的单一活动,因此当我按下Home键时,我只需替换其中一个片段。如何在不使用带有ACTIVITY_CLEAR_TOP标志的startActivity的情况下清除我的后退堆栈?

避免使用后退栈!它对整体效率并没有真正帮助!在导航时使用普通的replace(),甚至更好的是每次移除/添加!请查看我在https://dev59.com/qG025IYBdhLWcg3wyJCV#26093368上的帖子。 - stack_ved
16个回答

471

我在这里发了类似问题

根据Joachim的回答,Dianne Hackborn提出:

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42

我最终只使用了如下代码:

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {    
    fm.popBackStack();
}

但也可以使用类似下面这样的东西:

((AppCompatActivity)getContext()).getSupportFragmentManager().popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE)

这会将所有直到指定状态的状态都弹出。然后,您只需用所需内容替换片段即可。


9
嗯,它相当于点击返回按钮一次或多次,因此会更改当前可见的片段。(至少在我尝试过的情况下是这样) - Peter Ajtai
8
我和Peter遇到了相同的问题。我想清除所有的碎片,而不是让它们循环,这会产生许多影响。例如,通过依次弹出堆栈中的每个碎片,您将触发一些不必要的生命周期事件。 - Brian Griffey
253
要返回顶部,只需使用:fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); - Warpzit
54
就我所知,使用 fragmentManager. popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 对我来说效果更好,因为它可以防止片段动画执行。 - roarster
18
无法正常工作 - 这将触发介于每个片段之间的 onStart 方法的调用。 - James
显示剩余8条评论

197

为了回复@Warpzit的评论并让其他人更容易找到,以下是建议:

使用:

fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

16
我相信,当使用AppCompatActivity时,以这种方式从后退栈中弹出所有片段在最新的v7-appCompat库中已经失效。将我的应用更新到最新的v7-appCompat库(21.0.0)并扩展新的AppCompatActivity后,以上述方式弹出片段将使一些片段留在FragmentManager的后退栈记录中。我建议不要使用这种方法。 - dell116
2
可以使用 popBackStackImmediate。 - Kannan_SJD
我认为这应该是正确的答案,但当我从片段 AB 再到 C 并在 C 上调用此方法返回到 A 时,这只能工作一次。当我再次从 A 经由 BC 并触发 fragmentManager.popBackStackImmediate 时,它不会再次导航到 A。我使用 getParentFragmentManager().beginTransaction().replace(x, y).addToBackStack(null).commit();AB,从 BC 进行导航。有什么想法如何解决我的问题吗? - CodeNinja

48

对于所有相关方的尊重,我很惊讶看到你们中有多少人可以通过简单地使用 fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 清除整个片段回退栈。

根据Android文档(关于“name”参数 - 在所声称的工作方案中的“null”):

如果为null,则只会弹出顶部状态。

现在,我确实意识到我缺乏你们特定实现方面的知识(例如,在给定时间点上你们在返回栈中有多少条目),但是当期望在更广泛的设备和供应商范围内获得良好定义的行为时,我会押下所有的钱支持被接受的答案:

(供参考,类似于这样的东西)

FragmentManager fm = getFragmentManager(); // or 'getSupportFragmentManager();'
int count = fm.getBackStackEntryCount();
for(int i = 0; i < count; ++i) {    
    fm.popBackStack();
}

4
对我来说,每次点击返回按钮都会内部跳转到每个片段的 onCreateView 方法。在某些资源上可能会收到 NPE 错误。但是使用 fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 可以简单地清除所有返回栈。 - sud007
2
有没有关于如何在使用循环弹出时跳过onCreateView调用的见解? - Ayush Goyal
清除最顶层的片段(null)将会清除在其后面的所有片段。 - 2b77bee6-5445-4c77-b1eb-4df3e5
1
文档似乎表明,仅在这种情况下弹出顶部片段,但实际上,在使用POP_BACK_STACK_INCLUSIVE标志时,实现确实会清除整个后堆栈。使用POP_BACK_STACK_INCLUSIVE时,FragmentManager.popBackStackState()中的逻辑会跳过弹出单个条目的name==null逻辑,并继续清除所有条目。https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/FragmentManager.java#2529 - Egg
1
@Egg 是的,多年来 - 由于令人惊讶的许多开发人员提出了与文档中所述实际经验不同的建议 - 我也逐渐相信“代码”和“书本”之间必须存在差异。 - dbm

26

清除回退栈而不循环

String name = getSupportFragmentManager().getBackStackEntryAt(0).getName();
getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE);

其中name是addToBackStack()方法的参数

getSupportFragmentManager().beginTransaction().
                .replace(R.id.container, fragments.get(titleCode))
                .addToBackStack(name)

即使您替换了.fragment,堆栈仍将存在,但不可见。 - Asthme

23

适合我并且不使用循环的简单方法:

 FragmentManager fragmentManager = getSupportFragmentManager();
 //this will clear the back stack and displays no animation on the screen
 fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

18

接受的答案对我来说不够,我必须使用:

FragmentManager fm = getSupportFragmentManager();
int count = fm.getBackStackEntryCount();
for(int i = 0; i < count; ++i) {
    fm.popBackStackImmediate();
}

6
这是在另一个回答中指出的,但为了确保注意到:如果您在类似这样的循环中弹出整个堆栈,您将触发从堆栈开始到结束之间每个Fragment的生命周期方法。在大多数情况下,这种实现很可能会产生意想不到的后果。还值得指出的是,popBackStackImmediate()同步执行事务,一般来说不推荐使用。 - Damien Diehl

14

嗨~我发现了一种更好的解决方案,来源于:https://gist.github.com/ikew0ng/8297033

    /**
 * Remove all entries from the backStack of this fragmentManager.
 *
 * @param fragmentManager the fragmentManager to clear.
 */
private void clearBackStack(FragmentManager fragmentManager) {
    if (fragmentManager.getBackStackEntryCount() > 0) {
        FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(0);
        fragmentManager.popBackStack(entry.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }
}

2
请在回答中详细说明为什么这个解决方案更好。 - Simon.S.A.
1
它使用sdk本身提供的机制,不会引起一些npe问题。 - Chenxi

11

我想要补充一点:

使用以下方法从后退栈中弹出:

fragmentManager.popBackStack()

只是将片段从事务中移除,绝不会将其从屏幕中删除。 因此,理论上,它可能对您不可见,但可能有两个或三个片段在彼此之上堆叠,在按返回键时,UI 可能看起来混乱、重叠。

举个简单的例子:

假设您有一个 FragmentA,使用 fragmentmanager.replace() 加载 Fragmnet B,然后我们执行 addToBackStack,以保存此事务。 所以流程是:--

步骤1 -> FragmentA->FragmentB(我们转到 FragmentB,但 FragmentA 在后台,不可见)。

现在,您在 fragmentB 中进行了一些工作,并按下“保存”按钮,此后应返回 fragmentA。

步骤2-> 在保存 FragmentB 后,我们回到 FragmentA。

步骤3-> 因此,常见的错误是... 在 FragmentB 中,我们将 fragment Manager.replace() 的 fragmentB 替换为 fragmentA。

但实际上正在发生什么,我们再次加载 FragmentA,将 FragmentB 替换掉。现在有两个 FragmentA(一个来自 STEP-1,另一个来自 STEP-3)。

两个 FragmentA 实例被堆叠在彼此之上,可能是不可见的,但它确实存在。

因此,即使我们通过上述方法清除了后退栈,事务已经清除,但实际的片段并没有被清除。 因此,在这种特殊情况下,按保存按钮时,您只需通过 fm.popBackStack()fm.popBackImmediate() 返回到已经在内存中的 fragmentA 即可。

因此,正确的第三步-> fm.popBackStack() 返回到已经在内存中的 fragmentA。


9

针对 Kotlin 开发者:

repeat(supportFragmentManager.backStackEntryCount) {
    supportFragmentManager.popBackStack()
}

4
只需使用此方法,并传递Context和Fragment标签,即可删除我们需要删除的后退栈片段。 用法
clearFragmentByTag(context, FragmentName.class.getName());



public static void clearFragmentByTag(Context context, String tag) {
    try {
        FragmentManager fm = ((AppCompatActivity) context).getSupportFragmentManager();

        for (int i = fm.getBackStackEntryCount() - 1; i >= 0; i--) {
            String backEntry = fm.getBackStackEntryAt(i).getName();
            if (backEntry.equals(tag)) {
                break;
            } else {
                 fm.popBackStack();
            }
        }
    } catch (Exception e) {
        System.out.print("!====Popbackstack error : " + e);
        e.printStackTrace();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接