解析包含片段的布局时出现异常

3
我正在开发一个有趣的色轮应用程序(颜色轮应用)。第0.5版包含一个调色板,由四个片段组成,分别对应于颜色轮上可能的四种颜色目标。这些片段的UI元素根据颜色轮的模式而隐藏或显示。"单色调"模式只需要其中一个片段,而"重音类比"模式则需要全部四个片段。这个方法很好用,因为这些片段是作为onCreate()方法的一部分构建和显示的,并且以后从未被接触过。
为了改善布局,在下一个版本中,我设计了四个“include”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]
        ...
1个回答

0
由于一个或多个片段正在被重用,所以会出现如下异常。 是的,异常来自于填充具有与已在布局中的片段相同ID(最可能)的片段的新布局。为了避免这种情况,您有几个选项,例如: 您可以使用FragmentManager查找当前存在的片段并将它们从布局中删除,然后再填充带有片段的新布局。例如,如果您目前只有一个片段的布局,并且想要填充两个片段的布局,则首先需要删除两个布局中都存在的片段:
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.fragment1)).commit();
getSupportFragmentManager().executePendingTransactions();
mContainer.removeAllViews();
// inflate thew new layout

但是如果我处在你的境地,我不会使用那个(主要是因为你正在处理静态片段,这是不应该做的)。现在,我不知道你将如何放置这些片段,但更有意义的做法是(并且可以避免未来其他片段相关问题),拥有一个主布局,其中包含每个片段的占位符容器(简单的FrameLayouts)。所有片段都将在onCreate方法中创建,因此您避免了每次用户更改布局时重新实例化它们。这样,在运行时,您只需要隐藏所需的片段,并在必要时修改包装容器的参数。通过这种方式,您还将获得免费的片段管理(保持片段状态也将变得更加简单),如果活动面临配置破坏(例如旋转电话)。


我首先要感谢@Luksporg指出片段的静态性质。他提出的解决方案对我的应用程序不适用。我的布局视觉是动态的,因此片段不是正确的选择。我将片段重构为视图,解决了我的问题。然而,我现在的想法是希望返回到片段,并使用自定义布局来在各种显示模式之间进行过渡。 - Dobbo
@Dobbo 我的布局设计理念是动态的,因此片段不是最佳选择。 - 我认为你误解了Fragment框架的目的。片段不仅仅是视图包装器/容器。它们被引入作为可重用的行为单元,开发人员可以更好地处理它们。如果布局本质上是动态的,您可以使用片段,更不用说在像平板电脑这样的大屏幕上,它们可以提供更好的用户体验。 - user
@Dobbo 我做了一个小样例,随机更改4个布局,每个布局都包含4个片段中的一些。你可以在这里找到它 https://gist.github.com/4362865 。你也可以看一下嵌套片段。如果这没有帮助,那么也许我误解了你的问题,我会删除我的回答。 - user

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接