PopBackStack 但将第一个片段保留在 android

Posted

技术标签:

【中文标题】PopBackStack 但将第一个片段保留在 android【英文标题】:PopBackStack but keep the first fragment in android 【发布时间】:2015-04-06 07:24:24 【问题描述】:

我在做fragment transaction,backstack是这样的:

fragA => fragB => fragC => fragD

从fragD回来后我想回到fragA

fragD => onBackPress => fragA

所以,我尝试了如下代码:

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

但它清除了所有的 backstack ,我怎样才能将第一个片段保留在 backstack 中?非常感谢

【问题讨论】:

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

例如您可以执行以下操作:

添加 fragA 而不将其添加到 backStack。所以它总是在 活动,并且不会对后退按钮做出反应。 当你打开 fragD 时,你应该clear fragment BackStack。所以当你按下 D 片段的返回按钮时,你会回到 A。

附:还有其他方法可以做你想做的事。这取决于...

【讨论】:

我清除了整个后台堆栈,然后添加回 fragA 和 fragD 。谢谢 很多人都会犯这个错误,请记住您正在将事务添加到后台堆栈中,因此通常不应将添加流中第一个片段的事务添加到后台堆栈中(您为什么要想回到空状态?)【参考方案2】:

因为“后退堆栈”具有类似堆栈的行为...后进先出...您添加到后退堆栈的最后一个片段将首先从后退堆栈中弹出。您将需要通过指定您自己的来手动实现所需的行为。使用FragmentManager 类方法并不难。

如果您在将片段添加到事务时“标记”它们...

 fragmentTransaction.add(new FragmentA(), "FragmentA_Tag");

您可以稍后确定在按下返回按钮时显示哪个片段...

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

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

【讨论】:

【参考方案3】:

backstack 包含有关事务的记录,而不是片段本身。 所以你不应该将第一笔交易的记录(null -> fragA)添加到backstack。 并且所有其他交易的记录都应该添加到 backstack 中。 在这种情况下,您 preformpopBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); android 删除了除 fragA 之外的所有片段,因为没有任何关于如何添加 fragA 的记录。

【讨论】:

【参考方案4】:

就在几天前,我开始学习 Android 中的 Fragment。我也遇到了这个问题。在这里,我展示了我的解决方案以及我如何解决这个问题。如果我的代码不正确,请修复。这个时候我们有什么?活动,许多片段和他们的后台。我们希望从 Drawer 菜单中打开每个片段,并从 backstack 中清除所有其他片段。但是,我们必须只持有一个 Home 片段。当我们停留在主页片段并且用户按下返回按钮时,应用程序正在关闭。

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;

【讨论】:

【参考方案5】:

看了很多帖子,我是这样想的:

在 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。

【讨论】:

【参考方案6】:

我已经完成了以下方式。清除所有片段然后添加第一个家庭片段

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

            
        

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

我知道现在回答您的问题为时已晚,但这可能对其他人有所帮助。 由于我这几天一直在寻找解决方案...我已经使用 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();

【讨论】:

【参考方案10】:

您可以覆盖 onbackpressed (Mainactivity) 并使用片段管理器的 getBackStackEntryCount(),您可以检查它是否不等于 1,并且仅在该条件下 popbackstack。

@Override
public void onBackPressed() 
  if (getSupportFragmentManager().getBackStackEntryCount() > 0) 
     if (!(getSupportFragmentManager().getBackStackEntryCount() == 1)) 
      getSupportFragmentManager().popBackStack() ;
    
      


【讨论】:

以上是关于PopBackStack 但将第一个片段保留在 android的主要内容,如果未能解决你的问题,请参考以下文章

Realm保留一个已删除的对象

在 PopBackStack 之后片段弹出调用 OnViewCreated

片段 PopBackStack

popBackStack导致一次又一次调用片段的oncreateView

如何知道何时调用了`navController.popBackStack()`?

BottomNavigation popBackStack() 导航到 startDestination 而不是上一个片段