无法保留嵌套片段

Posted

技术标签:

【中文标题】无法保留嵌套片段【英文标题】:Can't retain nested fragments 【发布时间】:2013-01-28 19:45:30 【问题描述】:

还有另一种保存嵌套片段状态的方法吗? 或者如果我们不应该这样做,为什么?谢谢!

02-13 11:42:43.258: E/androidRuntime(7167): java.lang.IllegalStateException: Can't retain fragements that are nested in other fragments
02-13 11:42:43.258: E/AndroidRuntime(7167):     at android.support.v4.app.Fragment.setRetainInstance(Fragment.java:742)

【问题讨论】:

嗯。我没有像你这样的例外。对我来说,父项中的 onCreateView 只是从 findFragmentById 获取一个新的子片段。这意味着它需要重新初始化,这对于 Google MapFragment 来说真的很慢。 :( 请注意,在code.google.com/p/android/issues/detail?id=197271修复后,这不再是问题 在支持库24中发布 这仍然是个问题。 【参考方案1】:

您可以使用FragmentManager.saveFragmentInstanceState(Fragment) 检索片段状态。返回值实现了 Parcelable,所以你可以把它放在一个 Bundle 中。

对于恢复,您可以使用Fragment.setInitialSavedState(Fragment.SavedState)提供创建片段后的状态。

【讨论】:

看源码,这个并没有解决这个问题。 setRetainInstance 允许您保留任意类字段,甚至是不可打包、不可序列化的字段。但是,此方法仅在您的片段上调用 ​​onSaveInstanceState(Bundle),这意味着您所能做的就是保存可以放入 Bundle 中的内容。 @Matthias 确实,但在大多数的情况下,这正是您所需要的。经常使用setRetainInstance,而不是实施适当的状态管理。 据我所知,如果在父片段上设置保留实例为真,则实际上不需要保留内部片段。为什么 ?因为它也会自动保留孩子。 @Andrew 这就是为什么在嵌套片段中尝试setRetainInstance 时会得到:unable to retain the instance of the children of nested fragments【参考方案2】:

由于支持库 20+ (https://code.google.com/p/android/issues/detail?id=74222),子片段的实例重新创建存在错误,有一个修复 - http://ideaventure.blogspot.com.au/2014/10/nested-retained-fragment-lost-state.html

来自网页的代码(将此添加到您的父片段) -

private FragmentManager childFragmentManager() //!!!Use this instead of getFragmentManager, support library from 20+, has a bug that doesn't retain instance of nested fragments!!!!
        if(mRetainedChildFragmentManager == null) 
            mRetainedChildFragmentManager = getChildFragmentManager();
        
        return mRetainedChildFragmentManager;
    

    @Override
    public void onAttach(Activity activity) 
        super.onAttach(activity);

        if (mRetainedChildFragmentManager != null) 
            //restore the last retained child fragment manager to the new
            //created fragment
            try 
                Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFMField.setAccessible(true);
                childFMField.set(this, mRetainedChildFragmentManager);
             catch (NoSuchFieldException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
            
        
    
 @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setRetainInstance(true);

    

【讨论】:

同上,请不要使用反射。或者至少证明这是唯一的方法。 @NecipAllef,和上面一样——如果你说事情不好——提供更好的解决方案。只有当你知道它存在时才投反对票。【参考方案3】:

在AOSP commit之后,这不再是最新支持库的限制。

以下是提交信息:

在嵌套片段上允许 setRetainInstance(true)

将配置更改中的任意嵌套片段保存为 非配置对象。这允许使用保留实例 子片段作为其他中的任意不透明依赖项 碎片。

【讨论】:

这基本正确,新的*支持库更新适用于 7.0 和 5.0 及更低版本,但在 Android 6.0 上无法正常运行。使用保留片段的策略将变得更加规范,因为在 7.0 上,您只需通过 onSaveInstance 保存 1mb 的数据【参考方案4】:

问题: mChildFrgamentManager 正在重新创建 (https://code.google.com/p/android/issues/detail?id=74222)解决方法: 如果片段具有 setRetainInstance(true),则保留 mChildFrgamentManager

@Override
public void onAttach(Activity activity) 
    super.onAttach(activity);

    if (getRetainInstance()) 
        if (mRetainedChildFragmentManager != null) 
            try 
                Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFMField.setAccessible(true);
                childFMField.set(this, mRetainedChildFragmentManager);
             catch (NoSuchFieldException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
            
         else 
            mRetainedChildFragmentManager = getChildFragmentManager();
        
    

警告:使用此代码,setRetainInstace 应在 onAttach 之前调用。

P.S:这是@attels 答案的一些改进版本。

【讨论】:

@akshayrajkore “之前”是什么意思?这是一个片段的onAttach 方法。 @NecipAllef 为什么?如果您有一个没有反思的解决方法 - 提出它。如果不是 - 请删除反对票。 @akshayrajkore,我误解了你的问题。 setRetainInstance 应该在片段构造函数中调用。 @NecipAllef 为什么我应该使用Activity 而不是Fragment?问题不在于使用什么,而在于如何解决问题。 @NecipAllef,这是解决问题的方法——这是事实,因为它解决了问题。这个解决方案有多好 - 是另一个问题。由于没有提供更好的解决方案 - 对可行的解决方案投反对票是一种非常糟糕的感觉。特别是如果你连一个问题都不懂。

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

将片段添加到片段中(嵌套片段)

使用嵌套片段和动画对象

共享元素转换在父片段和子片段之间不起作用(嵌套片段)

当我将片段添加到我的视图寻呼机时(在嵌套滚动视图中),我无法从具有设备后退按钮的应用程序退出

从另一个片段打开一个片段始终保留前一个片段标题

访问嵌套片段的文本字段