在Android中弹出回退栈但保留第一个Fragment

17

我正在处理碎片事务,并且返回栈是这样的:

fragA => fragB => fragC => fragD

我希望从fragD返回fragA

fragD => onBackPress => fragA

所以,我尝试了这样的代码:

getChildFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

但它清除了整个后退栈,我该如何保留第一个片段在后退栈中?非常感谢。


1
这里的答案:https://dev59.com/rW3Xa4cB1Zd3GeqPahFj#28115271 是最简单和最好的解决方法!在我的情况下,它适用于多个片段(可以在第一个片段之后以任何顺序启动),并且按下返回键将始终返回到第一个创建的片段。非常重要的一点是,在创建第一个片段时不要添加到backstack,但在添加所有其他片段时添加到backstack。 - Jemshid
10个回答

24

例如,你可以执行以下操作:

  • 添加fragA但不将其添加到backStack中。这样它将始终存在于activity中,并且不会对返回键产生反应。
  • 当你打开fragD时,你应该清除fragment BackStack。所以,当你从D frag按下返回按钮时,你会回到A。

P.S. 还有其他方法可以实现你想要的内容。这取决于...


2
我清除整个返回堆栈,然后添加回fragA和fragD。谢谢。 - user782104
很多人都会犯这个错误,请记住,您正在将事务添加到后退栈中,因此通常不应将流程中第一个片段的添加事务添加到后退栈中(为什么要返回到空状态?) - hmac

4
由于“返回堆栈”具有类似堆栈的行为,即后进先出,因此您添加到返回堆栈中的最后一个片段将首先从返回堆栈中弹出。您需要通过手动指定自己所需的行为来实现此行为。这不难使用FragmentManager类方法来完成。
如果您在将片段添加到事务中时将其“标记”,则可以轻松地通过名称引用它们,并根据需要执行任何操作。
 fragmentTransaction.add(new FragmentA(), "FragmentA_Tag");

当用户按下后退按钮时,您可以稍后确定要显示哪个片段...

FragmentA f = fragmentManager.findFragmentByTag("FragmentA_Tag");
if(f != null){
    f.show();
}

你如何确定显示哪个片段完全取决于你。你可以跟踪当前可见的片段,或者使用Fragment类的isHidden方法...顺便说一下,我在这里谈论的是原生片段,而不是支持库的片段。

4

后退栈包含有关事务的记录,而不是片段本身。

因此,您不应将第一个事务的记录(null -> fragA)添加到后退栈中。

所有其他事务的记录都应添加到后退栈中。

在这种情况下,当您执行popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);时,Android会除了fragA之外所有片段,因为没有任何记录说明如何添加fragA。


2

我最近开始学习Android中的碎片(Fragment),也遇到了这个问题。在这里,我展示了我的解决方案以及如何解决这个问题。如果我的代码有误,请修正。我们现在有什么?一个Activity,许多Fragment和它们的后退栈。我们想要从抽屉菜单中打开每个Fragment并清除后退栈中的所有其他Fragment。但是,我们必须只保留一个Home Fragment。当我们停留在Home Fragment时,用户按下返回按钮应用程序将关闭。

Activity.class

protected void onCreate(Bundle savedInstanceState)
{
    ...
    // adding Home fragment without adding transaction into backstack
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.container, HomeFragment.newInstance("args"), null);
    ft.commit();
}

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        finish();
    }
}

public void addFragmentFromMenu(Fragment fragment){
    String backStateName =  fragment.getClass().getName();
    clearBackStack();
    FragmentManager manager = getSupportFragmentManager();
    if(manager.getBackStackEntryCount()> 0)
    {
        boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);

        if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
            //fragment not in back stack, create it.
            addFragment(fragment, manager, backStateName);
        }
    }
    else // no fragments
    {
        addFragment(fragment, manager, backStateName);
    }
}

public void addFragment(Fragment fragment, FragmentManager manager, String backStateName)
{
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.container, fragment, backStateName);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
}

public void clearBackStack() {
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}

当点击抽屉菜单项时

@Override
public boolean onNavigationItemSelected(MenuItem item) {

    int id = item.getItemId();

    if (id == R.id.nav_camera) {
        addFragmentFromMenu(CameraFragment.newInstance("cam1", "cam2"));
    } else if (id == R.id.nav_gallery) {
        addFragmentFromMenu(TestFragment.newInstance("test1","test2"));
    }

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

1

在查看了许多帖子后,我找到了这种方法:

在 fragC => fragD 方法中,执行两个事务:

1 清除返回堆栈,fragC => fragA

2 fragA => fragD

但是这样做可能会破坏 fragA 的原始状态。

public static void changeFragCtoD(FragmentManager fm, Fragment fragD){
    fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction
            .replace(R.id.containerViewId, new fragAClass())
            .commit();

    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction
            .replace(R.id.containerViewId, fragD)
            .addToBackStack(fragD.getClass().getName())
            .commit();
}

现在在fragD页面按返回键可以回到fragA页面。

0
我已经按照以下方式完成了。清除所有片段,然后添加第一个主页片段。
FragmentManager fm = getActivity().getSupportFragmentManager();
                    fm.popBackStack(Constants.TAG_HOME, FragmentManager.POP_BACK_STACK_INCLUSIVE);

                    ((MainActivity) activity).manageFragment(new HomeFragment(), Constants.TAG_HOME);
 //        write down below function in main activity              
     public void manageFragment(Fragment fragment, String tag) {

            FragmentManager fragmentManager = getSupportFragmentManager();
            if (!fragmentManager.popBackStackImmediate(tag, 0)) {

                FragmentTransaction ft = fragmentManager.beginTransaction();
                ft.add(R.id.content_frame, fragment, tag);
                ft.addToBackStack(tag);
                ft.commit();

            }
        }

0

1)使用以下代码添加第一个片段

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
        android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
        if (fm.findFragmentById(R.id.fragment_container) != null) {
            ft.hide(fm.findFragmentById(R.id.fragment_container));
        }
        ft.add(R.id.fragment_container, new OneFragment(),OneFragment.class.getCanonicalName())
                .addToBackStack(OneFragment.class.getCanonicalName()).commit();

2) 使用以下代码从第一个片段添加第二个片段

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                    android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                    if (fm.findFragmentById(R.id.fragment_container) != null) {
                        ft.hide(fm.findFragmentById(R.id.fragment_container));
                    }
                    ft.add(R.id.fragment_container,new TwoFragment(),TwoFragment.class.getCanonicalName())
.addToBackStack(TwoFragment.class.getCanonicalName()).commit();

3) 使用以下代码从第二个片段添加第三个片段

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                if (fm.findFragmentById(R.id.fragment_container) != null) {
                    ft.hide(fm.findFragmentById(R.id.fragment_container));
                }
                ft.add(R.id.fragment_container, new ThreeFragment(),ThreeFragment.class.getCanonicalName())
                        .addToBackStack(ThreeFragment.class.getCanonicalName()).commit();

4) 使用以下代码从第三个片段添加第四个片段

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                if (fm.findFragmentById(R.id.fragment_container) != null) {
                    ft.hide(fm.findFragmentById(R.id.fragment_container));
                }
                ft.add(R.id.fragment_container, new FourFragment(),ThreeFragment.class.getCanonicalName())
                        .addToBackStack(FourFragment.class.getCanonicalName()).commit();

5) 请在下面编写onBackPressed()代码

@Override
    public void onBackPressed() {
        hideKeyboard(MainActivity.this);
        Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.fragment_container);

        if (currentFragment.getClass().getName().equalsIgnoreCase(FourFragment.class.getName())) { // Using this code come from third fragment to first fragment
            Fragment f = this.getSupportFragmentManager().findFragmentByTag(TwoFragment.class.getCanonicalName());
            if (f != null) {
                this.getSupportFragmentManager().popBackStackImmediate(f.getClass().getCanonicalName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
        }else {
            super.onBackPressed();
        }
    }

0

记住我的话,完整的FragmentTransaction被添加到后退栈中,不仅仅是一个片段,这意味着即使您在单个事务中添加和删除片段,调用poBackStack()也会反转完整的事务。在其参数中传递标记会弹出所有事务,直到标记的事务,甚至弹出标记的事务(如果将FragmentManager.POP_BACK_STACK_INCLUSIVE添加到参数中) 因此,更多的是关于您如何添加它而不是如何删除它。 请参见执行片段事务


0

我知道这已经晚了,对于你的问题回答来说,但它可能对其他人有所帮助。

因为我过去几天一直在寻找解决方案... 我用 for 循环解决了它,我认为这是最简单的方法。

首先,以这种方式初始化一个整数

int count = getSupportFragmentManager().getBackStackEntryCount();

其中getBackStackEntryCount()将计算事务的数量。

然后在调用fragD中的FragmentTransaction方法之前,添加此for循环。

for (int i = 0; i < count; i++){
                getSupportFragmentManager().popBackStack();
            }

for循环可以为您完成任务,而popBackStack()将使您返回到fragA

它看起来像这样

int count = getSupportFragmentManager().getBackStackEntryCount();
            for (int i = 0; i < count; i++){
                getSupportFragmentManager().popBackStack();
            }
            fragmentTransaction.replace(R.id.frame_layout, new FoodFragment(), "Food").addToBackStack(null);
            fragmentTransaction.commit();

0
你可以重写onBackPressed(MainActivity),并使用FragmentManager的getBackStackEntryCount()方法,检查它是否不等于1,只有在这种情况下才能弹出回退栈。
@Override
public void onBackPressed() {
  if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
     if (!(getSupportFragmentManager().getBackStackEntryCount() == 1)) {
      getSupportFragmentManager().popBackStack() ;
    }
  }    
}


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