Android 膨胀包含碎片的布局时出现异常

Android 膨胀包含碎片的布局时出现异常,android,android-fragments,layout-inflater,Android,Android Fragments,Layout Inflater,我正在开发一个有趣的游戏。版本0.5有一个由四个片段组成的调色板,每个片段对应色轮上可能的颜色目标。然后根据色轮的模式隐藏或显示片段的UI元素。“单色”模式只需要一个片段,而“重音合成”模式则需要全部四个片段。当片段作为onCreate()方法的一部分被构建和显示时,就会发现这一点,此后再也不会被触及 对于下一个改进布局的版本,我设计了四个“包含”XML布局文件,一个只使用片段的一个实例,一个使用两个实例,依此类推。主XML布局文件现在有一个FrameLayout,当模式更改时,我在这里替换相应

我正在开发一个有趣的游戏。版本0.5有一个由四个片段组成的调色板,每个片段对应色轮上可能的颜色目标。然后根据色轮的模式隐藏或显示片段的UI元素。“单色”模式只需要一个片段,而“重音合成”模式则需要全部四个片段。当片段作为
onCreate()
方法的一部分被构建和显示时,就会发现这一点,此后再也不会被触及

对于下一个改进布局的版本,我设计了四个“包含”XML布局文件,一个只使用片段的一个实例,一个使用两个实例,依此类推。主XML布局文件现在有一个
FrameLayout
,当模式更改时,我在这里替换相应的“include”XML布局文件

以下是执行更改的代码片段:

private void changeLayout(int layoutID) {
    FragmentManager fMgr = getFragmentManager();
    FragmentTransaction transaction = fMgr.beginTransaction();

    LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    FrameLayout frame = (FrameLayout) findViewById(R.id.paletteContainer);
    frame.removeAllViews();

    View view = inflater.inflate(layoutID, frame, false);
    frame.addView(view);
    transaction.commit();

    pFrag = (SwatchFragment.Primary) fMgr.findFragmentById(R.id.base);
    a1Frag = (SwatchFragment.FirstAlternate) fMgr.findFragmentById(R.id.alternate1);
    a2Frag = (SwatchFragment.SecondAlternate) fMgr.findFragmentById(R.id.alternate2);
    cFrag = (SwatchFragment.Complementary) fMgr.findFragmentById(R.id.complementary);    
}
由于一个或多个片段正在被重用,因此下面给出了一些异常。那么,替换包含片段的UI部分的正确方法是什么呢

E/WheelActivityandroid.view.InflateException: Binary XML file line #11: Error inflating class Navigation item selected: pos=1, id=0x00000001
android.view.InflateException: Binary XML file line #11: Error inflating class fragment
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704) ~[na:0.0]
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:746) ~[na:0.0]
        at android.view.LayoutInflater.inflate(LayoutInflater.java:489) ~[na:0.0]
        at android.view.LayoutInflater.inflate(LayoutInflater.java:396) ~[na:0.0]
        at org.dobbo.colour.activity.WheelActivity.changeLayout(WheelActivity.java:321) ~[na:0.0]
        at org.dobbo.colour.activity.WheelActivity.setMode(WheelActivity.java:307) ~[na:0.0]
        at org.dobbo.colour.activity.WheelActivity.onNavigationItemSelected(WheelActivity.java:181) ~[na:0.0]
        at com.android.internal.widget.ActionBarView$1.onItemSelected(ActionBarView.java:148) ~[na:0.0]
        at android.widget.AdapterView.fireOnSelected(AdapterView.java:892) ~[na:0.0]
        at android.widget.AdapterView.access$200(AdapterView.java:49) ~[na:0.0]
        at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:860) ~[na:0.0]
        at android.os.Handler.handleCallback(Handler.java:725) ~[na:0.0]
        at android.os.Handler.dispatchMessage(Handler.java:92) ~[na:0.0]
        at android.os.Looper.loop(Looper.java:137) ~[na:0.0]
        at android.app.ActivityThread.main(ActivityThread.java:5039) ~[na:0.0]
        at java.lang.reflect.Method.invokeNative(Native Method) ~[na:0.0]
        at java.lang.reflect.Method.invoke(Method.java:511) ~[na:0.0]
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) ~[na:0.0]
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) ~[na:0.0]
        at dalvik.system.NativeStart.main(Native Method) ~[na:0.0]
Caused by: java.lang.IllegalArgumentException: Binary XML file line #11: Duplicate id 0x7f0a000b, tag null, or parent id 0x7f0a0010 with anoth
er fragment for org.dobbo.colour.fragment.SwatchFragment$Primary
        at android.app.Activity.onCreateView(Activity.java:4722) ~[na:0.0]
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680) ~[na:0.0]
        ...
由于一个或多个片段是 重复使用

是的,这个例外来自于膨胀新的布局,其中包含与布局中已经存在的片段具有相同ID(很可能)的片段。要避免这种情况,您有几个选项,例如:

您可以使用
FragmentManager
查找当前存在的片段,并在使用片段膨胀新布局之前将其从布局中删除。例如,如果当前布局中只有一个片段,并且希望使用两个片段对布局进行充气,则应首先移除两个布局中存在的片段:

getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.fragment1)).commit();
getSupportFragmentManager().executePendingTransactions();
mContainer.removeAllViews();
// inflate thew new layout

但如果我是在你的情况下,我不会使用它(主要是因为你在处理静态片段,这是你不应该做的)。现在,我不知道您将如何放置这些片段,但是如果有一个主布局,它将为每个片段保存占位符容器(简单的
框架布局
),这将更有意义(并且可以在将来避免与片段相关的其他问题)(所有片段都将在
onCreate
方法中创建,这样您就可以避免每次用户更改布局时都恢复这些片段)。这样,您在运行时所要做的就是隐藏所需的片段,并在必要时修改包装容器的参数。这样您还可以获得免费的片段管理(如果活动将面临配置破坏(如旋转手机时),则维护片段状态也将非常容易).

首先,我要感谢@Luksporg指出了片段的静态本质。他提出的解决方案不适合我的应用程序。我对布局的看法本质上是动态的,因此片段不是解决问题的方法。我将片段重新构建成视图,解决了我的问题。然而,我现在的想法就是这个愿望返回到片段并使用自定义布局在各种显示模式之间转换。@Dobbo我对布局的看法本质上是动态的,因此片段不是最佳选择。-我认为您误解了
片段
框架的目的。片段不仅仅是(或)视图包装器/容器。它们是作为可重用的行为单元引入的,开发人员可以更好地处理它们。如果布局本质上是动态的,您可以使用片段,更不用说,与它们一起使用,您可以在更大的屏幕上提供更好的用户体验,就像平板电脑一样。@Dobbo我制作了一个小示例,其中包括随机生成4个布局,这四个布局中的每一个都包含4个片段中的一些。您可以在这里找到它。您还可以查看嵌套片段。如果这没有帮助,那么可能我误解了您的问题,我将删除我的答案。