如何只保留第一个添加的片段在后栈(片段重叠)?

时间:2013-01-10 23:46:29

标签: android

情景我想要实现的目标:

  1. 使用两个框架容器加载活动(有关项目列表和详细信息)。
  2. 在应用启动时,在listFrame中添加listFragment,在detailsFrame容器中添加一些初始infoFragment。
  3. 浏览列表项而不将每个详细信息事务添加到后台堆栈(只想在堆栈中保留infoFragment)。
  4. 一旦用户点击后退按钮(向后导航),他就会回退到初始信息片段。启动时添加的内容。
  5. 如果顺序返回导航,则应用退出。
  6. 我的代码:

            protected override void OnCreate(Bundle savedInstanceState)
            {
    ...
                var listFrag = new ListFragment();
                var infoFrag = new InfoFragment();
                var trans = FragmentManager.BeginTransaction();
                trans.Add(Resource.Id.listFrame, listFrag);
                trans.Add(Resource.Id.detailsFrame, infoFrag);
                trans.Commit();
    ...
            }
    
            public void OnItemSelected(int id)
            {
                var detailsFrag = DetailFragment.NewInstance(id);
                var trans = FragmentManager.BeginTransaction();
                trans.Replace(Resource.Id.detailsFrame, detailsFrag);
                if (FragmentManager.BackStackEntryCount == 0)
                    {
                        trans.AddToBackStack(null);
                    }
                trans.Commit();
            }
    

    我的问题:

    在按下后退按钮后,infoFrag与之前的detailFrag重叠!为什么呢?

5 个答案:

答案 0 :(得分:11)

你可以这样做:

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
   getSupportFragmentManager().popBackStack(getSupportFragmentManager().getBackStackEntryAt(0).getId(), getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
} else {
   super.onBackPressed();}

在你的活动中,你要保留第一个片段。

你的第一个片段中不应该有addToBackStack。但是,其余的,是的。

答案 1 :(得分:6)

问题是您backing的交易有两个步骤:

  • 删除infoFrag
  • 添加detailsFrag(这是已添加的第一个详细信息容器)

(我们知道因为文档This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

因此,每当系统恢复时,一个事务正在恢复这两个步骤,并且它没有对添加到它的最后一个detailFrag说什么,所以它不会对它做任何事情。

我可以根据你的情况考虑两种可能的工作方式:

  1. 将您的活动引用到最后使用的detailsFrag并使用BackStackChange监听器,只要值从1更改为0(您必须跟踪以前的值),您还会删除剩下的一个片段

  2. 在每个点击监听器上,您必须popBackStackImmediatly()(删除上一个事务)和addToBackStack()所有事务。在此变通方法中,您还可以使用一些setCustomAnimation魔法来确保它在屏幕上看起来都很漂亮(例如,使用0到0持续时间1的alpha动画,以避免先前的片段出现并再次消失。

  3. PS。我同意片段管理器/事务应该对它处理.replace()动作的堆栈的方式更加聪明,但这就是它的方式。

    修改

    正在发生的事情是这样的(我正在为细节添加数字以使其更清晰)。 请记住.replace() = .remove().add()

    Transaction.remove(info).add(detail1).addToBackStack(null) // 1st time
    Transaction.remove(detail1).add(detail2) // 2nd time
    Transaction.remove(detail2).add(detail3) // 3rd time
    Transaction.remove(detail3).add(detail4) // 4th time
    

    现在我们在布局上有详细信息4:

    < Press back button >
         System pops the back stack and find the following back entry to be reversed
             remove(info).add(detail1);
         so the system makes that transaction backward.
         tries to remove detail1 (is not there, so it ignores)
         re-add(info) // OVERLAP !!!
    

    所以问题是系统没有意识到有一个detail4,并且事务是.replace()它应该替换那里的任何内容。

答案 2 :(得分:6)

Budius非常好的解释。我阅读了他的建议并实施了类似的导航,我想与其他人分享。

而不是像这样替换片段:

Transaction.remove(detail1).add(detail2)
Transaction.remove(detail2).add(detail3)
Transaction.remove(detail3).add(detail4)

我在活动布局文件中添加了一个片段容器布局。它可以是LinearLayoutRelativeLayotFrameLayout等。所以在创建活动中我有这个:

transaction.replace(R.id.HomeInputFragment, mainHomeFragment).commit();

mainHomeFragment是我想按下后退按钮时要返回的片段,例如infoFrag。然后,在我进行每次下一次交易之前:

fragmentManager.popBackStackImmediate();
transaction.replace(R.id.HomeInputFragment, frag2).addToBackStack(null).commit();

fragmentManager.popBackStackImmediate();
transaction.replace(R.id.HomeInputFragment, frag3).addToBackStack(null).commit();

通过这种方式,您无需跟踪当前显示的片段。

答案 3 :(得分:0)

您可以覆盖onBackPressed并将事务提交到初始片段。

答案 4 :(得分:0)

我猜测但是:

您已添加该事务以将带有1st detailsFrag的infoFrag替换为backstack。

但是你用1st detailsFrag替换1st detailsFrag。

此时单击返回时,片段管理器无法使用infoFrag干净地替换1st detailsFrag,因为1st detailsFrag已被删除并替换。

是否预期重叠行为我不知道。

我建议调试Android核心代码,看看它在做什么。

我不确定你是否可以在没有覆盖Activity::onBackPressed()的情况下实现,并且自己将所有交易添加到后台堆中。