9

我想要实现的场景:

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

4

5 回答 5

12

你可以这样做:

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

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

在您的第一个片段中,您不应该有 addToBackStack。但是,在其他方面,是的。

于 2015-01-23T17:19:27.950 回答
7

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

而不是像这样替换片段:

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

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

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

这样您就不必跟踪当前显示的片段。

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

附言。我同意片段管理器/事务应该更聪明地处理它在 .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() 它应该替换那里的任何东西。

于 2013-01-11T17:12:00.017 回答
0

我猜但是:

您已添加事务以将 infoFrag 替换为第一个 detailsFrag 到后台堆栈。

但随后您将第一个 detailsFrag 替换为第二个 detailsFrag。

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

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

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

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

于 2013-01-11T11:15:27.233 回答
0

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

于 2013-01-10T23:55:36.493 回答