替换 Fragment 时出现 IllegalStateException 异常

44

这是一个使用兼容包的小型Android 2.2测试应用程序。以下是我(错误的)尝试在接收点击时替换Fragment的方式。我试图用同一Fragment类的新(不同)实例替换它。正如我将要解释的那样,它不像预期的那样工作,我需要帮助:

public class MainFragmentActivity extends FragmentActivity {
  ...

  public void myAction(View view) {
    ...
    RightFragment newRightFrag = RightFragment.newInstance(myNewOption);
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
    ft.replace(R.id.landscape_right_fragment, newRightFrag);
    ft.commit();
  }
}

你肯定已经看到了我的错误。无论如何,让我们更详细地解释一下应用程序应该做什么:

横向屏幕方向:

---------                ----------
| L | R |  -> click ->   | L | R2 |
---------                ----------

在横屏模式下,该活动具有包含2个片段的视图:“leftLand”和“rightLand”,如果你单击片段“leftLand”的一个按钮,则会创建一个新的片段,并将“rightLand”片段实例替换为同一FragamentActivity类的另一个实例。这两个实例之间的区别是传递给“newInstance(int)”方法的参数,它基于单击的按钮。

纵向方向:

-----                  -----
|   |                  |   |
| L |   -> click ->    | R |
|   |                  |   |
-----                  -----

在竖屏模式下,它只显示片段"leftPort"(与"leftLand"布局相同),如果您点击其按钮,则启动一个Intent并启动RightFragmentActivity,该Activity显示片段 "rightLand"。

如果我不替换右侧片段,它可以正常工作。但是,如果我这样做了(在横屏模式下单击按钮),则在后续方向更改(重新启动Activity)时,由于"IllegalStateException: Fragment RightFragment did not create a view"错误,FragmentActivity无法启动

D/AndroidRuntime( 1428): Shutting down VM
W/dalvikvm( 1428): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
E/AndroidRuntime( 1428): FATAL EXCEPTION: main
E/AndroidRuntime( 1428): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.agm.test/com.agm.test.MainFragmentActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class fragment
E/AndroidRuntime( 1428):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
E/AndroidRuntime( 1428):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
E/AndroidRuntime( 1428):        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3815)
E/AndroidRuntime( 1428):        at android.app.ActivityThread.access$2400(ActivityThread.java:125)
E/AndroidRuntime( 1428):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2037)
E/AndroidRuntime( 1428):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 1428):        at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 1428):        at android.app.ActivityThread.main(ActivityThread.java:4627)
E/AndroidRuntime( 1428):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 1428):        at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 1428):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/AndroidRuntime( 1428):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/AndroidRuntime( 1428):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 1428): Caused by: android.view.InflateException: Binary XML file line #13: Error inflating class fragment
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:582)
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
E/AndroidRuntime( 1428):        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:198)
E/AndroidRuntime( 1428):        at android.app.Activity.setContentView(Activity.java:1647)
E/AndroidRuntime( 1428):        at com.agm.test.MainFragmentActivity.onCreate(MainFragmentActivity.java:25)
E/AndroidRuntime( 1428):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
E/AndroidRuntime( 1428):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
E/AndroidRuntime( 1428):        ... 12 more
E/AndroidRuntime( 1428): Caused by: java.lang.IllegalStateException: Fragment com.agm.test.RightFragment did not create a view.
E/AndroidRuntime( 1428):        at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:287)
E/AndroidRuntime( 1428):        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:558)
E/AndroidRuntime( 1428):        ... 21 more
W/ActivityManager(   59):   Force finishing activity com.agm.test/.MainFragmentActivity
我意识到旧的 "RightFragment" 在被替换后没有被销毁。这可能是我错误地尝试替换它的结果。
非常感谢任何帮助。
提前致谢!
/ Angel Galindo Muñoz

你的初始RightFragment是通过编程还是XML添加的?如果你试图删除(或替换)在XML中定义的片段,你可能会遇到麻烦。 - theisenp
感谢您的快速回复,theisenp。我已经阅读了一些类似的文章,但我认为它只适用于我将片段替换为其他片段类的实例的情况。今天下午我会尝试以编程方式解决这个问题,并发布结果。非常感谢! - Angel
我又回来了,但还是没解决问题。我试图使用编程方式,在RightFragment.onCreateView中使用“inflater.inflate(...)”进行操作。我将视图减少到更简单的内容,但仍然失败:只是LinearLayout中的一个TextView。发现了一些重要的东西:每次我按下按钮并更改TextView的文本时,“它并没有完全替换掉原来的内容”,而是只是在上面绘制新的内容。这证实了在“ft.replace(...)”并提交后,原始的RightFragment实例从未被销毁。我也会非常感激至少给我提供一个正确替换Fragment的链接。非常感谢! - Angel
2个回答

95
我认为你可能误解了我的评论,所以我会在这里提供更详细的解释。
一个常见的问题是,当尝试删除或替换片段时,往往会遇到将片段通过XML添加到布局中而不是在Java中以编程方式添加的情况。这与在片段的Java代码的onCreateView()函数中膨胀片段自己的布局相同(这是您在对我的评论做出回应时似乎描述的情况)。为了说明我在说什么,我将向您展示人们尝试删除/替换片段的两种方法。 这是错误的方法: XML布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

    <fragment android:name="com.example.ExampleFragment"
    android:id="@+id/example_fragment"
    android:layout_weight="1"
    android:layout_width="0dp"
    android:layout_height="match_parent" />

</LinearLayout>

Java:

swapFragment()
{
    Fragment newFragment = new ExampleFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

这段代码不会按照你的期望执行。在XML布局中添加的初始片段不会被删除。这是因为XML布局旨在描述静态布局元素。您可以在运行时更改它们的内容或隐藏它们,但无法从布局中删除这些元素。这就是我之前链接到的讨论线程中Dianne Hackborn所说的。

以下是正确的做法(至少在我的经验中):

XML布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

    <!-- Fragment will go here eventually, but it's not added in the layout -->

</LinearLayout>

Java:

protected void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);
     setContentView(R.layout.my_layout);

     ExampleFragment newFragment = new ExampleFragment();
     FragmentTransaction transaction = getFragmentManager().beginTransaction();
     transaction.add(R.id.fragment_container, newFragment);
     transaction.commit();
}

...

swapFragment()
{
    Fragment newFragment = new ExampleFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

此策略不会在初始布局中添加片段。相反,在创建活动时,它将在Java代码中添加。这使得它可以从布局中删除(使用 remove() replace())。这可能无法解决您的问题,但这是片段创建的常见困难。您可以确保以适当的方式添加片段以允许它们被删除,如果这不能解决问题,我们可以进一步进行故障排除。

我又回来了,我确认这个问题已经解决了!!!非常感谢theisenp。你帮了我很大的忙,现在我对它有了更深刻的理解。很遗憾我没有足够的声望来投票支持你的回答,但肯定会有其他人会这么做的。现在我有点开心了 ;) 来自西班牙巴塞罗那的问候 / Angel Galindo - Angel
@theisenp,我在横屏模式下遇到了IllegalArgumentException异常:找不到id为0x7f050006的视图,用于片段RightFragment {44ee2af0 #3 id = 0x7f050006}。你能告诉我安卓何时会引发这个异常吗? - Bipin Vayalu
@BipinVayalu,听起来你可能正在尝试引用一个不存在的视图,但是没有看到你的实际代码很难确定。你能否创建一个新的问题,并附上你的 Activity 和 Fragment 代码以及你正在使用的纵向和横向布局? - theisenp
对我没用...我得到了一个新的错误: 从方法b.a引用 - user3402040
onCreate 中,应该仅在第一次运行时添加初始片段,即如果 savedInstanceState == null。 如果它不为 null,则在 super.onCreate 调用期间自动重新创建了片段。 - Miha_x64
显示剩余5条评论

0

还有另一种方法,当您为具有大屏幕的平板电脑开发应用程序时,可以为每个屏幕方向充气不同的布局。您只需要创建两个布局并将它们命名为纵向或横向,在onCreate中根据方向简单地充气xml。

在onClick事件中,只需使用以下代码识别方向:getResources().getConfiguration().orientation

然后进行您的操作


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