片段中的片段

Posted

技术标签:

【中文标题】片段中的片段【英文标题】:Fragments within Fragments 【发布时间】:2011-10-14 10:00:54 【问题描述】:

我想知道这是否真的是 android API 中的一个错误:

我有这样的设置:

┌----┬---------┐
|    |         |
|  1 |    2    |
|    |┌-------┐|
|    ||       ||
|    ||   3   ||
└----┴┴-------┴┘
    是在右侧窗格中加载片段 #2(搜索屏幕)的菜单。 是一个搜索屏幕,其中包含片段#3,它是一个结果列表。 结果列表在多个地方使用(包括作为其自身功能的高级片段)。

此功能在手机上运行良好(其中 1 & 2 和 3 是ActivityFragments)。

但是,当我使用这段代码时:

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();       
    Fragment frag = new FragmentNumber2();
    if(toLoad != null) frag.setArguments(toLoad);
    transaction.replace(R.id.rightPane, frag);      
    transaction.commit();

其中R.id.leftPaneR.id.rightPane 是水平线性布局中的<fragment>s。

据我了解,上面的代码删除了常驻的片段,然后用新的片段替换它。太棒了...显然这不是发生的情况,因为当此代码第二次运行时,您会收到以下异常:

07-27 15:22:55.940: ERROR/AndroidRuntime(8105): Caused by: java.lang.IllegalArgumentException: Binary XML file line #57: Duplicate id 0x7f080024, tag null, or parent id 0x0 with another fragment for FragmentNumber3

这是因为 FragmentNumber3 的容器已复制,并且不再具有唯一 ID。在添加新的 Fragment 之前,最初的 Fragment 没有被销毁 (?)(在我看来,这意味着它没有被 替换)。

有人能告诉我这是否可能(this answer 表示不可能)还是一个错误?

【问题讨论】:

@rds 这是一个古老的问题,标记为重复有点毫无意义。 Example project demonstrating a fragment within a fragment 【参考方案1】:

目前不支持嵌套片段。尝试将片段放在另一个片段的 UI 中会导致未定义且可能会损坏的行为。

更新:从 Android 4.2(和 Android 支持库 rev 11)开始支持嵌套片段:http://developer.android.com/about/versions/android-4.2.html#NestedFragments

注意(根据 this docs):“注意:当布局包含 <fragment> 时,您不能将布局扩展为片段。嵌套片段仅在添加到动态的片段。"

【讨论】:

不支持,因为它不是初始实现的设计目标。我听到了很多对该功能的要求,所以它可能会在某个时候完成,但像往常一样,还有很多其他事情在优先考虑。 我通过扩展 FragmentActivity、FragmentManager 和 FragmentTransaction 来管理它。基本前提是在我的活动中扩展 DeferringFragmentActivity,提供相同的 api,因此无需更改其他代码。当我调用getFragmentManager 时,我得到一个DeferringFragmentManager 实例,当我调用beginTransaction 时,我得到一个DeferredTransaction。此事务存储带有调用方法和参数的 POJO。当调用 commit 时,我们首先查找任何挂起的 DeferredTransactions。提交所有事务后,我们开始一个真正的事务并使用 args 运行所有存储的方法。 现在就是这一点。嵌套的Fragments 现在是 Android API 的一部分,耶! developer.android.com/about/versions/…. 哇,真是一场噩梦:如果您在 Fragment 上使用 ,并且该 Fragment 碰巧使用了子 Fragment,它不会因明显错误而失败(“无法添加子碎片布局片段”)——它神秘地失败了,出现了“片段没有创建视图”之类的异常。有几个小时的调试时间...... @MartínMarconcini 当然,但根据 API 提供的功能,这一点并不明显。如果某事不允许,则应该清楚地记录在案,而不是让开发人员因为某些事情没有按您期望的方式工作而将他们的头发拉出来。【参考方案2】:

android 4.2 及更高版本支持嵌套片段

Android 支持库现在还支持嵌套片段,因此您可以在 Android 1.6 及更高版本上实现嵌套片段设计。

要嵌套片段,只需在要添加片段的片段上调用 ​​getChildFragmentManager()。这将返回一个 FragmentManager,您可以像通常从***活动中一样使用它来创建片段事务。例如,下面是一些从现有 Fragment 类中添加片段的代码:

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

要了解有关嵌套片段的更多信息,请阅读这些教程 Part 1 Part 2 Part 3

这是一个讨论 best practices for nested fragments 的 SO 帖子。

【讨论】:

Nestedfragment 的主要缺点是我们不能从 childfragment 调用 optionmenu :( 如果我们使用 ABS! 你能看看我的问题吗?它非常相似..***.com/questions/32240138/…。对我来说,子框架网络没有从代码中膨胀【参考方案3】:

.. 你可以在父片段的destroyview 方法中清理你的嵌套片段:

@Override
    public void onDestroyView() 

      try
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.remove(nestedFragment);

        transaction.commit();
      catch(Exception e)
      

        super.onDestroyView();
    

【讨论】:

如果您使用 SetAlwaysFinish (bricolsoftconsulting.com/2011/12/23/…) 进行一些生命周期测试,您会看到当另一个活动在顶部并启用始终完成时,此代码会导致错误(IllegalStateException:无法执行此操作在 onSaveInstanceState 之后)。将上面的代码包装在 try / catch 中并不是最优雅的解决方案,但它似乎可以让一切正常。 这几乎奏效了。后来我得到了一个关于绘制 UI 的 ***。绝对避免嵌套片段...【参考方案4】:

我有一个正在开发的应用程序,其布局类似于启动片段的操作栏中的选项卡,其中一些片段中有多个嵌入的片段。

我在尝试运行应用程序时遇到了同样的错误。似乎如果您在取消选择选项卡然后重新选择后在 xml 布局中实例化片段,我会收到充气机错误。

我解决了这个问题,用 Linearlayouts 替换 xml 中的所有片段,然后使用片段管理器/片段事务来实例化片段,一切似乎至少在测试级别上正常工作。

希望对你有所帮助。

【讨论】:

谁能评论这种方法的有效性?我发现只能使用一个级别的 Fragments 是很不幸的——那时还不如根本不使用它们。以编程方式将它们添加到占位符视图组中将不会有任何警告? 似乎仍然对我有用,我将它们换入和换出视图也没有问题。需要注意的是,我只在蜂窝上这样做,不兼容冰淇淋三明治。【参考方案5】:

我也遇到过同样的问题,为此苦苦挣扎了几天,应该说最简单的解决方法是在 tab 时使用 fragment.hide() / fragment.show()选择/取消选择()。

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)

    if (mFragment != null)
        ft.hide(mFragment);

当屏幕旋转发生时,所有父片段和子片段都会被正确销毁。

这种方法还有一个额外的优势 - 使用 hide()/show() 不会导致片段视图失去其状态,因此不需要为 ScrollViews 恢复之前的滚动位置。

问题是我不知道在不可见的情况下不分离碎片是否正确。我认为 TabListener 的官方示例在设计时考虑到片段是可重用的,你不应该用它们污染内存,但是,我认为如果你只有几个选项卡并且你知道用户会经常在它们之间切换它将适合让他们与当前活动保持联系。

我想听听更有经验的开发人员的 cmets。

【讨论】:

【参考方案6】:

如果您发现您的嵌套片段没有被删除或被复制(例如,在 Activity 重新启动、屏幕旋转时)尝试更改:

transaction.add(R.id.placeholder, newFragment);

transaction.replace(R.id.placeholder, newFragment);

如果上面没有帮助,请尝试:

Fragment f = getChildFragmentManager().findFragmentById(R.id.placeholder);

FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

if (f == null) 
    Log.d(TAG, "onCreateView: fragment doesn't exist");
    newFragment= new MyFragmentType();
    transaction.add(R.id.placeholder, newFragment);
 else 
    Log.d(TAG, "onCreateView: fragment already exists");
    transaction.replace(R.id.placeholder, f);

transaction.commit();

学习here

【讨论】:

以上是关于片段中的片段的主要内容,如果未能解决你的问题,请参考以下文章

操作栏标签片段中的片段?

片段对话框中的片段膨胀引发错误“片段未创建视图”

当前一个片段中的某些任务完成时如何通知另一个片段中的适配器

如何处理片段和活动中的后压

从另一个片段中的目录更新片段中的列表视图元素

ViewPager2 中的片段在返回父片段时被重新创建