【问题标题】:How to keep only first added Fragment in back stack (fragment overlapping)?如何在后堆栈中只保留第一个添加的片段(片段重叠)?
【发布时间】:2013-01-10 23:46:28
【问题描述】:

我想要实现的场景:

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

我的代码:

        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 重叠!为什么?

【问题讨论】:

  • 这个答案在这里:- stackoverflow.com/a/28115271/9969285 是最简单和最好的解决方法!在我的情况下,它适用于多个片段(可以在第一个片段之后以任何顺序启动),并且后按将始终返回到第一个创建的片段。非常重要的一点要记住,不要在创建第一个 Fragment 时添加到 backstack,而是在添加所有其他 Fragment 时添加到 backstack。由于声誉点较少,我无法直接评论或投票给该答案。
  • 其简单的一行代码转到此链接并检查 stackoverflow.com/questions/14971780/…>

标签: android


【解决方案1】:

你可以这样做:

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

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

您不应该在您的第一个片段中使用 addToBackStack。 但是,在其余部分中,是的。

【讨论】:

  • '你不应该有,在你的第一个片段中,addToBackStack'应该是大写的,闪烁的箭头指向它!非常感谢!!
  • 所以,我将它添加到容器活动的onBackPressed。然后,我将addFragment 用于Frag A,然后replaceFragment + addToBackStack 用于Frag B 以保留Frag A,然后replaceFragment 用于Frag C。在 Frag C 上按返回可返回 Frag A。 (痛苦地躺在床上
【解决方案2】:

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】:

问题是您来自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. 在每个点击监听器上,您都必须 popBackStackImmediate()(删除前一个事务)和所有事务上的 addToBackStack()。在这个解决方法中,您还可以使用一些 setCustomAnimation 魔法来确保它在屏幕上看起来很好(例如,使用从 0 到 0 持续时间 1 的 alpha 动画来避免之前的片段再次出现和消失。

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

所以现在我们在布局上有了 detail4:

< 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() 它应该替换其中的任何内容。

【讨论】:

  • 我不太确定我是否理解您对问题的解释。如果我去 [infoFrag] > [detailsFrag] 然后按回我按预期跳回 [infoFrag]。当 [infoFrag] > [detailsFrag] > [detailsFrag] 然后返回时出现问题。另一个案例:stackoverflow.com/questions/10065640/…
  • 让我在我的答案中添加一个交易流的东西。给我几分钟
  • “所以问题是系统没有意识到有一个细节4”,这解释了一切。现在情况可以理解了,谢谢!只是好奇,您是如何获得这种启蒙的;)?
  • 我刚刚为我工作的公司完成了开发,作为我们内部库的一部分,一些片段事务的小控制器,就像在这个问题中一样,并检查我的答案 stackoverflow.com/questions/14053730/… 所以直到我到达正确的地方,有很多试验和错误并阅读源代码,我基本上是根据需要以多种不同的方式重新做后台堆栈。您是否了解我提出的可能的解决方法?如果你需要我可以进一步解释。
  • 似乎文档中关于删除所有当前碎片的部分具有误导性 - 仅删除了一个碎片! “这与为 所有当前添加的片段调用 remove(Fragment) 基本相同,这些片段使用相同的 containerViewId 添加,然后使用此处给出的相同参数添加(int, Fragment, String)。”
【解决方案4】:

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

【讨论】:

  • 实际上我不想与 Back Pressed 回调交互,因为我想让该按钮按预期工作,除了从信息片段应用程序导航 beck 必须退出(即第一个 beck 堆栈事务)。
【解决方案5】:

我猜是:

您已将使用第一个 detailsFrag 替换 infoFrag 的事务添加到后台堆栈中。

然后你用第二个 detailsFrag 替换第一个 detailsFrag。

此时当您单击返回时,片段管理器无法将第一个 detailsFrag 干净地替换为 infoFrag,因为第一个 detailsFrag 已被删除和替换。

我不知道是否会出现重叠行为。

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

我不确定您是否可以在不说覆盖 Activity::onBackPressed() 并自己将所有事务添加到后台堆栈的情况下执行弹出窗口的情况下实现。

【讨论】:

  • 从我看来,重叠行为根本不是预期的 :) 但是 BackStack 管理呢,这就是它的全部目的,即手动(显式)在/从后堆栈中添加或弹出事务,或者不是它?!如果我将第一笔交易放在后台堆栈中,我希望它在返回导航后就在那里,但不止一个。
  • 也许是 here 的用途
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-02
  • 2012-07-06
  • 2015-09-30
  • 1970-01-01
相关资源
最近更新 更多