当留下深度嵌套的堆栈时,这是清理Fragment back堆栈的正确方法吗?


130

我正在使用Android兼容性库来实现片段,并扩展了布局示例,以便片段包含触发另一个片段的按钮。

在左侧的选择窗格中,我有5个可选项目- A B C D E

每个都FragmentTransaction:replace在详细信息窗格中加载一个片段(通过)-a b c d e

现在,我扩展了片段e以包含一个按钮,该按钮e1也可以在详细信息窗格中加载另一个片段。我已经对fragment e的onClick方法进行了如下操作:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

如果我进行以下选择:

E - e - e1 - D - E

然后,片段e在详细信息窗格中。这很好,我想要的。但是,如果此时我按下back按钮,则无济于事。我必须单击两次,因为e1它仍在堆栈中。此外,单击后,在onCreateView中出现了空指针异常:

为了“解决”这个问题,无论何时A B C D E选择,我都添加了以下内容:

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

只是想知道这是正确的解决方案还是我应该做些其他的事情?

Answers:


252

好吧,有几种方法可以实现,具体取决于预期的行为,但是此链接应为您提供所有最佳解决方案,毫不奇怪,该链接来自Dianne Hackborn

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

本质上,您有以下选择

  • 为您的初始后退状态使用名称,然后使用 FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE)
  • 使用FragmentManager.getBackStackEntryCount()/ getBackStackEntryAt().getId() 检索后堆栈上第一个条目的ID,然后使用 FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) 应该弹出整个后退堆栈...我认为文档只是错误的。(实际上,我想这并不涵盖您传入的情况POP_BACK_STACK_INCLUSIVE),

这是什么意思?FragmentManager.getBackStackEntryCount()/ getBackStackEntryAt()。getId()
dannyroa

4
2nd为我工作。清除整个堆栈的含义:getSupportFragmentManager()。popBackStack(getSupportFragmentManager()。getBackStackEntryAt(0).getId(),FragmentManager.POP_BACK_STACK_INCLUSIVE)
NickL 2012年

@JorgeGarcia是否有可能在popbackstack之后我们完成片段而无需重新启动较旧的片段。
duggu

请注意,由于popBackStack()是异步的,因此还有popBackStackImmediate()版本,这意味着清除不会在调用该方法的确切时间发生。
Genc

6
声称该组被禁止为垃圾邮件,并且我无法访问该链接:(有人在其他地方拥有资源吗?
EpicPandaForce

61

如果您不想弹出所有堆栈条目,则使用另一种干净的解决方案...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

这将首先清理堆栈,然后加载新的片段,因此在任何给定的点上,堆栈中将只有一个片段


1
这是非常糟糕:getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);一更换前(),因为正在加载的片段调用onCreateView()onActivityCreated()等恢复和Destroing片段瞬间是坏的。片段可以例如注册接收者。这会影响性能。
VasileM

@VasileM,您能否详细描述?在什么情况下不好?是因为的异步行为FragmentTransaction吗?错误片段堆栈的性能只有不好吗?
CoolMind

27

感谢Joachim的回答,我终于使用代码清除了所有后退堆栈条目。

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */

18
为什么在这里使用循环?对我来说FragmentManager.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE)清除了所有后退堆栈条目...
Marek

3
@Marek,因为有时并非应删除所有片段,因此适合使用循环。
CoolMind

1
@CoolMind,对我而言仍然没有意义。popBackStack(backStackId, INCLUSIVE)会将所有片段(状态)弹出回到backStackId,因此,一旦您将弹出的最低片段i弹出,所有较高的片段应同时弹出。那么循环的意义是什么?
LarsH

@LarsH,你是对的。请参阅stackoverflow.com/a/59158254/2914140。为了将其他片段弹出到必需的标签,我们应该首先使用addToBackStack(tag)添加该片段。
CoolMind

7

我已经为清理Backstack进行了大量研究,最后看到了Transaction BackStack及其管理。这是最适合我的解决方案。

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

上面的方法循环遍历Backstack中的所有事务,并一次将其立即删除。

注意:以上代码有时无法正常工作,由于此代码,我会遇到ANR,因此请不要尝试此操作。

更新 以下方法可从后堆栈中删除该“名称”的所有帧。

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • name如果为非null,则为要查找的前一个返回状态的名称;如果找到,将弹出该状态之前的所有状态。的
  • POP_BACK_STACK_INCLUSIVE标志可用于控制是否弹出指定状态。如果为null,则仅弹出最高状态。

我不知道您为什么一次要删除它们。为什么选择此解决方案而不是其他解决方案?是否有理由一次删除一次而不是一次全部删除?
miva2 2015年

没法一次删除Backstack的方法,请参阅developer.android.com/reference/android/app/…–
Lokesh

3

我使用的代码与使用while循环的代码类似,但是我在每个循环中都调用条目计数...所以我想它的速度要慢一些

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }

0

写在如何弹出片段关闭返回堆栈LarsH这里,我们可以从上向下的特异性目的标签(弹出几个片段与片段标记一起使用此方法):

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

用片段的标签替换“ frag”。记住,首先我们应该使用以下命令将片段添加到堆栈中:

fragmentTransaction.addToBackStack("frag")

如果我们使用添加片段addToBackStack(null),则不会以这种方式弹出片段。


-4
    // pop back stack all the way
    final FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
    int entryCount = fm.getBackStackEntryCount(); 
    while (entryCount-- > 0) {
        fm.popBackStack();
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.