使用片段清除回栈

Posted

技术标签:

【中文标题】使用片段清除回栈【英文标题】:Clear back stack using fragments 【发布时间】:2011-09-05 09:33:36 【问题描述】:

我将我的 android 应用程序移植到了蜂巢,并进行了一次大重构以使用片段。在我之前的版本中,当我按下 Home 按钮时,我会使用ACTIVITY_CLEAR_TOP 来重置后台堆栈。

现在我的应用程序只是一个包含多个片段的 Activity,所以当我按下 Home 按钮时,我只需替换其中的一个片段。如何在不必使用 startActivityACTIVITY_CLEAR_TOP 标志的情况下清除我的后台堆栈?

【问题讨论】:

避免使用后退栈!它对整体效率并没有真正的帮助!每次要导航时都使用普通的 replace() 甚至更好的删除/添加!查看我在***.com/questions/5802141/…的帖子 【参考方案1】:

I posted something similar here

来自 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)

这将弹出所有状态到指定的状态。然后你可以用你想要的替换片段

【讨论】:

嗯,相当于按了一次或多次后退键,所以它改变了当前可见的片段。 (至少当我尝试过时) 我和彼得有同样的问题。我想清除所有碎片,而不是让它循环通过它们,这会产生很多影响。例如,通过按顺序从堆栈中弹出每个片段,您将遇到不需要的生命周期事件。 返回顶部只需使用:fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 为了它的价值,使用 fragmentManager。 popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);对我来说效果更好,因为它阻止了片段动画的执行 这不能正常工作 - 它会触发对中间每个片段的 onStart 的调用【参考方案2】:

为@Warpzit 的评论做出回答,让其他人更容易找到。

用途:

fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

【讨论】:

我相信在最新的 v7-appCompat 库中使用 AppCompatActivity 时,以这种方式从后台弹出所有片段已被破坏。在将我的应用程序更新到最新的 v7-appCompat 库(21.0.0)并扩展新的 AppCompatActivity 后,以上述方式弹出片段会在 FragmentManager 的回栈记录中留下一些片段。我建议不要使用它。 可以使用 popBackStackImmediate。【参考方案3】:

充分尊重所有相关方;看到你们中有多少人可以通过一个简单的方法清除整个片段返回堆栈,我感到非常惊讶

fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

根据Android documentation(关于name 参数-声称的工作提案中的“null”)。

如果为null,则仅弹出顶部状态

现在,我确实意识到我对您的特定实现缺乏了解(例如在给定时间点您在后台堆栈中有多少条目),但我会将所有的钱都押在接受的答案上在更广泛的设备和供应商上定义明确的行为:

(供参考,与此相关)

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

【讨论】:

对我来说不是,因为每个 pop 都会将您内部带到每个片段的 onCreateView 。我会在哪里收到有关某些资源的 NPE。但是使用 fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 只是杀死了所有的后台堆栈。 关于在使用循环弹出时如何跳过 onCreateView 调用的任何见解? 清除最顶部的片段(null)将清除后面堆栈中的所有片段。【参考方案4】:

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

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

【讨论】:

【参考方案5】:

清除没有循环的backstack

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 堆栈也会存活但不可见【参考方案6】:

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

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

【讨论】:

这在另一个答案中被指出,但只是为了确保它被指出:如果你像这样在一个循环中弹出整个堆栈,你将触发每个 Fragment 的生命周期方法在堆栈的开始和结束之间。在大多数用例中,这种实现很有可能会产生意想不到的后果。还值得指出的是,popBackStackImmediate() 同步执行事务,这通常是不明智的。【参考方案7】:

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

【讨论】:

请扩展您的答案,说明为什么这个解决方案更好。 它使用sdk自己提供的机制,不会引起一些npe问题。【参考方案8】:

我只是想补充一下:--

使用以下方法从后台弹出

fragmentManager.popBackStack()

只是从事务中删除片段,绝不会从屏幕上删除片段。 因此,理想情况下,您可能看不到它,但可能有两三个片段相互堆叠,并且在按下后退键时,UI 可能看起来杂乱无章。

举个简单的例子:-

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

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

现在你在 fragmentB 中做一些工作并按下 Save 按钮——保存后应该返回 fragmentA。

STEP 2-> 在保存 FragmentB 时,我们返回 FragmentA。

STEP 3 ->所以常见的错误是......在片段B中,我们将片段Manager.replace()片段B与片段A。

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

FragmentsA 的两个实例相互堆叠,可能不可见,但确实存在。

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

所以正确的Step3-> fm.popBackStack() 回到已经在内存中的fragmentA。

【讨论】:

【参考方案9】:

阅读documentation 并研究片段 id 是什么,它似乎只是堆栈索引,所以这是可行的:

fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);

零 (0) 是堆栈的底部,因此弹出到它包括在内会清除堆栈。

CAVEAT:虽然上述方法在我的程序中有效,但我有点犹豫,因为 FragmentManager 文档从未真正声明 id 是堆栈索引。它会是有道理的,我所有的调试日志都表明它是,但也许在某些特殊情况下它不会?任何人都可以以一种或另一种方式确认这一点吗?如果是,那么上面是最好的解决方案。如果没有,这是替代方案:

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

【讨论】:

fragmentManager..getBackStackEntryAt(0).getId()代替0怎么样?即使 backstack 条目 id 在某个点与堆栈索引不同,这也应该有效。【参考方案10】:

只需使用此方法并将 Context & Fragment 标签传递给我们需要删除 backstake 片段。

用法

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();
    

【讨论】:

【参考方案11】:

它对我有用,试试这个:

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

【讨论】:

【参考方案12】:

对于这里的 kotlin 人:

repeat(supportFragmentManager.backStackEntryCount) 
    supportFragmentManager.popBackStack()

【讨论】:

【参考方案13】:

我是这样工作的:

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

【讨论】:

【参考方案14】:
    private void clearBackStack()
        SupportFragmentManaer fm = getSupportFragmentManager();
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    

调用这个方法会很简洁。

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

【讨论】:

【参考方案15】:
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;

【讨论】:

感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation 将通过展示为什么这是解决问题的好方法,并使其对有其他类似问题的未来读者更有用,从而大大提高其长期价值。请edit您的回答添加一些解释,包括您所做的假设。

以上是关于使用片段清除回栈的主要内容,如果未能解决你的问题,请参考以下文章

清除活动回栈[重复]

回栈事务后如何持久化分片数据?

片段回栈和替换

Android - 清除导航回栈

DialogFragment dismiss() 不弹出回栈

导航组件 NavDeepLinkBuilder 回栈