Android Reorder Fragment Backstack

Posted

技术标签:

【中文标题】Android Reorder Fragment Backstack【英文标题】: 【发布时间】:2014-03-07 02:01:12 【问题描述】:

我的导航抽屉中列出了许多页面/片段,用户可能会经常在这些页面/片段之间切换,我希望它们在后台堆栈中以便他们可以导航回来,但我只想要每个片段的一个实例后台堆栈,这样用户就不必疯狂地按回很多次来退出应用程序。我不知道如何在不删除页面的情况下有效地“重新排序”backstack。

目前,当我更改页面时,我正在使用此代码更改片段并确保它仅在后堆栈中一次

  if (mFragMgr == null) 
      mFragMgr = getSupportFragmentManager();
  

  String backStateName = fragmentDescriptor.name();
  boolean fragmentPopped = mFragMgr.popBackStackImmediate(backStateName, 0);
  if (!fragmentPopped)
      mFragMgr.beginTransaction()
      .remove((Fragment) mFragment)
      .replace(R.id.content_frame, (Fragment) mFragment)
      .addToBackStack(backStateName)
      .commit();
  

我在 onBackPressed 中使用此代码

  @Override
  public void onBackPressed() 
      if (mFragMgr.getBackStackEntryCount() > 0) 
          mFragMgr.popBackStackImmediate();
       else 
          super.onBackPressed();
      
  

这可行,但这意味着它会删除我不想删除的页面。示例:

当我的用户按 A > B > C > D > E > C 的顺序访问 6 个页面时,因为我正在执行删除操作,我预计会出现以下堆栈:

                            [E]    [C]
                     [D]    [D]    [E]
              [C]    [C]    [C]    [D]
       [B]    [B]    [B]    [B]    [B]
[A] -> [A] -> [A] -> [A] -> [A] -> [A]

但我实际上得到的是以下内容 - 它会将所有内容弹出到与名称匹配的元素,这与我是否包含“.remove((Fragment) mFragment)”无关 - (I '现在已经意识到 remove 不会影响 backstack,所以无需指出这一点):

                            [E]
                     [D]    [D]
              [C]    [C]    [C]    [C]
       [B]    [B]    [B]    [B]    [B]
[A] -> [A] -> [A] -> [A] -> [A] -> [A]

如果我在添加到后台堆栈时不使用名称而是使用 null,我会得到以下信息:

                                   [C]
                            [E]    [E]   
                     [D]    [D]    [D]    
              [C]    [C]    [C]    [C]   
       [B]    [B]    [B]    [B]    [B]  
[A] -> [A] -> [A] -> [A] -> [A] -> [A] 

我怎样才能得到我期望的行为?是否有可能,还是我需要自己记录更改并完全跳过后台堆栈?

【问题讨论】:

我正在尝试找到一种有效的方法来解决这个问题,我在我的问题上付出了一些努力 - 如果投票的人有勇气说出原因,那就太好了 抱歉,FragmentManager API 中没有用于从后台堆栈中间移除Fragment 状态的方法。 API 仅公开了用于推送和弹出的标准堆栈机制。 我认为可能是这样。但我的用例似乎是一个非常有效、相当普遍的需求,您可以通过活动实现类似的需求。我希望有人可能知道帮助类或实现此目的的东西 好吧,我不知道任何现有的解决方案,也不能不使用反射或自定义FragmentManager 实现来实现。如果您愿意这样做,那么它实际上应该很容易实现。 我想应该可以手动创建您自己的自定义Fragment 回栈,使用您需要的任何功能。您将无法使用 BackStackRecord 类,因为它取决于堆栈模型;它必须是一个定制的解决方案。如果您有兴趣,那么我可能会在几天内有时间(或者其他人可以)写一个基于此的答案。或者,如果您愿意,您也可以自己尝试——应该不会太难。 【参考方案1】:

没有 API 可以做到这一点,但因为上周我也在想同样的事情, 我把它当作一个练习,我自己实现了它。

我的方法允许您从后台堆栈中的任何位置删除Fragment,这是通过使用反射来修改将后台堆栈信息存储在FragmentManagerImpl 和BackStackRecord 中的内部变量来实现的。

有几个重要的变量:

mBackStack - 存储包含Fragment 信息的BackStackRecords、以前的信息、动画等 mActive - 全部添加 Fragments mAvailBackStackIndices - 存储新插入的记录可以使用的索引,即null 值的索引mActive 和其他

用 150+ Fragments 进行测试,没有发现任何泄漏,但只监视了 DDMS 中的堆,没有执行任何其他内存分析。 因此,由于某些内容可能被破坏,请探索代码,重新测试它,看看是否有人提供了更好的答案,然后重新考虑您是否真的需要在您的项目中这样做。

我以gist on GitHub 的形式上传了代码,希望对您有所帮助。

【讨论】:

一个有趣的方法,谢谢。需要大量代码才能使其工作,我将在本周晚些时候有更多时间时深入研究一下 正如我之前所说,我喜欢的代码有点太多了,但是,我相信这对于某些访问者来说是正确的解决方案,并且您需要付出一些努力,所以我会把赏金奖励给你!【参考方案2】:

当您想重用之前的片段时,只需先隐藏所有片段并一次显示所需的片段即可。通过这样做,您可以重用您的片段,而无需删除它们,也无需重新加载片段。但请确保在实现之前您必须将片段添加到 backstack。

【讨论】:

如果你有 2000 个片段,你最终会 OOM。

以上是关于Android Reorder Fragment Backstack的主要内容,如果未能解决你的问题,请参考以下文章

android 怎么开启fragment

android fragment和activity的区别

关于Android中fragment的管理

android fragment 刷新功能

android fragment和activity的区别

android中Fragment的切换方法。