使用碎片清理回栈


244

我将Android应用程序移植到蜂窝上,为了使用片段,我做了很大的重构。在以前的版本中,当我按下“主页”按钮时,我曾经做过一个操作ACTIVITY_CLEAR_TOP,以重置后退堆栈。

现在我的应用只是一个带有多个片段的Activity,因此当我按下Home按钮时,我只是替换其中的一个片段。如何不用startActivityACTIVITY_CLEAR_TOP标志一起使用而清除我的后排堆栈?


避免使用后置堆栈!它并不能真正提高整体效率!使用普通的replace(),甚至每次导航时最好删除/添加!检查我在stackoverflow.com/questions/5802141/…上的
stack_ved 2014年

Answers:


430

我在这里贴了类似的东西

从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)

这会将所有状态弹出到指定状态。然后,您可以将片段替换为所需的片段


8
好吧,这等效于单击“后退”按钮一次或多次,因此它会更改当前可见的片段。(至少当我尝试
过时

7
我和彼得有同样的问题。我想清除所有碎片,而不是让它们循环遍历,这会产生很多影响。例如,通过顺序地将每个片段从堆栈中弹出,您将遇到不需要的生命周期事件。
Brian Griffey 2012年

233
要转到顶部,只需使用:fragmentManager.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE);
2013年

53
对于其价值,使用fragmentManager。popBackStackImmediate(null,FragmentManager.POP_BACK_STACK_INCLUSIVE); 对我来说效果更好,因为它阻止了片段动画的执行
roarster 2013年

17
这不能正常工作-它将触发之间的每个片段的onStart调用
James

165

为@Warpzit的评论提供答案,并使其他人更容易找到它。

用:

fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

14
我相信在使用AppCompatActivity时,最新的v7-appCompat库中以这种方式从后退堆栈弹出的所有片段都已被打破。将我的应用程序更新到最新的v7-appCompat库(21.0.0)并扩展了新的AppCompatActivity之后,以上述方式弹出片段会将一些片段留在FragmentManager的后台记录中。我建议不要使用它。
2015年

可能使用popBackStackImmediate。
Kannan_SJD

38

充分尊重所有有关方面;我很惊讶地看到你们中有多少人可以通过简单的操作清除整个碎片

fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

根据Android文档(关于name论点-所主张的工作建议中的“空”)。

如果为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

1
关于使用循环弹出时如何跳过onCreateView调用的任何见解?
Ayush Goyal

清除最上面的片段(空)将清除后堆栈中紧随其后的所有片段。
2b77bee6-5445-4c77-b1eb-4df3e5

18

为我工作,不使用循环的简单方法:

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

17

接受的答案对我来说还不够。我不得不使用:

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

2
这是在另一个答案中指出的,但只是要确保它被指出:如果在这样的循环中弹出整个堆栈,则将触发堆栈开始和结束之间每个Fragment的生命周期方法。在大多数使用情况下,此实现很有可能会产生意想不到的后果。还值得指出的popBackStackImmediate()是,同步执行事务通常是不明智的。
Damien Diehl

16

清除没有循环的堆栈

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堆栈将仍然有效但不可见
哮喘病,

8

我只想补充:-

使用以下命令从堆栈弹出

fragmentManager.popBackStack()

只是要从事务中删除碎片,而无法从屏幕上删除碎片。因此,理想情况下,它可能对您不可见,但是可能有两个或三个片段彼此堆叠,并且在按回车键时,UI可能看起来很杂乱,堆叠。

仅举一个简单的例子:

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

步骤1-> FragmentA-> FragmentB(我们移至FragmentB,但Fragment A在背景中,不可见)。

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

步骤2->在保存FragmentB的同时,我们返回FragmentA。

步骤3->因此,常见错误将是...在片段B中,我们将使用片段A执行片段Manager.replace()片段B。

但是实际上发生了什么,我们再次加载Fragment A,代替FragmentB。因此,现在有两个FragmentA(一个来自STEP-1,一个来自此STEP-3)。

FragmentsA的两个实例彼此堆叠,这可能不可见,但在那里。

因此,即使我们确实通过上述方法清除了后栈,事务也会被清除,但不会清除实际的碎片。因此,理想情况下,在这种情况下,只需按一下保存按钮,只需执行fm.popBackStack()fm.popBackImmediate()即可返回fragmentA 。

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


3

阅读文档并研究片段ID是什么,它似乎只是堆栈索引,因此可以使用:

fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);

零(0)是堆栈的底部,因此弹出包含它的内容将清除堆栈。

CAVEAT:尽管上面的代码在我的程序中有效,但是我还是有些犹豫,因为FragmentManager文档从未真正指出id是堆栈索引。确实是这样,我的所有调试日志都没有,但是在某些特殊情况下不是吗?任何人都可以以一种或另一种方式确认这种情况吗?如果是这样,那么以上是最佳解决方案。如果没有,这是替代方案:

while(fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStackImmediate(); }

1
如何使用fragmentManager..getBackStackEntryAt(0).getId()而不是0?即使backstack条目ID在某些时候与堆栈索引不同,这也应该起作用。
ChristophK

3

只需使用此方法并传递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();
    }
}

2

嗨〜我找到了一个更好的解决方案,来自: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.SA

它使用sdk提供的机制,不会引起npe问题。
晨曦

1

我这样工作:

public void showHome() {
    getHandler().post(new Runnable() {
        @Override
        public void run() {
            final FragmentManager fm = getSupportFragmentManager();
            while (fm.getBackStackEntryCount() > 0) {
                fm.popBackStackImmediate();
            }
        }
    });
}

1

它对我有用,请尝试以下方法:

public void clearFragmentBackStack() {
        FragmentManager fm = getSupportFragmentManager();
        for (int i = 0; i < fm.getBackStackEntryCount() - 1; i++) {
            fm.popBackStack();
        }
    }

0
    private void clearBackStack(){
        SupportFragmentManaer fm = getSupportFragmentManager();
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

调用此方法将非常简洁。

  1. 无需循环。
  2. 如果您在片段中使用动画,它将不会显示太多动画。但是使用循环会。

0
private boolean removeFragFromBackStack() {
    try {
        FragmentManager manager = getSupportFragmentManager();
        List<Fragment> fragsList = manager.getFragments();
        if (fragsList.size() == 0) {
            return true;
        }
        manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

感谢您提供此代码段,它可能会提供一些有限的即时帮助。通过说明为什么这是一个很好的解决方案,正确的解释将大大提高其长期价值,并且对于其他存在类似问题的读者来说,它将更加有用。请编辑您的答案以添加一些解释,包括您所做的假设。
Abdelilah萨尔瓦多艾萨维
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.