替换片段和屏幕旋转

19
我正在开发一个针对2.x和3.0设备的Android应用程序,因此我正在使用兼容性API。我正在Android 2.0上进行测试。
我正在尝试将一个已显示的片段替换为另一个片段(搜索表单和搜索结果),当第二个(结果)片段被显示时,在方向更改时我遇到了崩溃问题。
基本上,我有一个活动,其中包括在布局xml中定义的一个片段。
    <fragment class="org.prevoz.android.search.SearchFormFragment"
              android:id = "@+id/search_form_fragment"
              android:layout_width = "fill_parent"
              android:layout_height = "fill_parent" />

默认情况下显示第一个片段(SearchFormFragment)。当用户点击“搜索”按钮时,我将SearchFormFragment替换为SearchResultsFragment,并使用AsyncTask运行它(这就是为什么我想保留它的原因)。

    // Show the search results fragment
SearchResultsFragment newSearch = new SearchResultsFragment(from, to, when);
newSearch.setRetainInstance(true);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
transaction.replace(R.id.search_form_fragment, newSearch);      
transaction.addToBackStack(null);
transaction.commit();

然而,当搜索表单(SearchFormFragment)被显示并且方向被改变时,我的应用程序会崩溃,错误信息为:

ERROR/AndroidRuntime(334): FATAL EXCEPTION: main
ERROR/AndroidRuntime(334): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.prevoz.android/org.prevoz.android.search.MainActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class fragment
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3815)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.access$2400(ActivityThread.java:125)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2037)
ERROR/AndroidRuntime(334):     at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(334):     at android.os.Looper.loop(Looper.java:123)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.main(ActivityThread.java:4627)
ERROR/AndroidRuntime(334):     at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(334):     at java.lang.reflect.Method.invoke(Method.java:521)
ERROR/AndroidRuntime(334):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
ERROR/AndroidRuntime(334):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
ERROR/AndroidRuntime(334):     at dalvik.system.NativeStart.main(Native Method)
ERROR/AndroidRuntime(334): Caused by: android.view.InflateException: Binary XML file line #13: Error inflating class fragment
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:582)
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
ERROR/AndroidRuntime(334):     at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:198)
ERROR/AndroidRuntime(334):     at android.app.Activity.setContentView(Activity.java:1647)
ERROR/AndroidRuntime(334):     at org.prevoz.android.search.MainActivity.onCreate(MainActivity.java:40)
ERROR/AndroidRuntime(334):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
ERROR/AndroidRuntime(334):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
ERROR/AndroidRuntime(334):     ... 12 more
ERROR/AndroidRuntime(334): Caused by: java.lang.IllegalStateException: Fragment org.prevoz.android.search.SearchFormFragment did not create a view.
ERROR/AndroidRuntime(334):     at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:281)
ERROR/AndroidRuntime(334):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:558)
ERROR/AndroidRuntime(334):     ... 21 more
WARN/ActivityManager(59):   Force finishing activity org.prevoz.android/.search.MainActivity

从SearchFormFragment中相关的onCreateView代码确实被调用了,并且我返回了一个有效的视图:

@Override
public View onCreateView(LayoutInflater inflater, 
                         ViewGroup container,
                         Bundle savedInstanceState) 
{       
    View newView = inflater.inflate(R.layout.search_form_frag, container, false);
    return newView;
}

只有在显示SearchResultsFragment时才会发生崩溃,如果显示默认的SearchFormFragment,则更改正常工作。

那么我如何通过方向更改保留第二个片段的状态呢?关于API和状态更改的文档确实缺乏信息。

3个回答

28
不要在XML中创建SearchFormFragment。相反,使用空的FrameLayout,并在Activity.onCreate()中填充它而不将其添加到后退栈中。这样,Activity将保留当前的Fragment,而不是尝试添加XML中指定的一个。
此外,使用AsyncLoader可能是更好的方法,请参见http://code.google.com/p/android/issues/detail?id=14944

4

在苦苦挣扎了多个小时后,我终于解决了这个问题。问题并不在设置或我们调用 Fragment 类的方式上,而是与屏幕显示错误消息有关。如果你在 Fragment 类的 onCreateView() 方法中检查容器是否为空,则会收到“无法膨胀片段”消息,而不是容器为空的消息。因此,在 onCreateView() 方法中不要检查空容器。确保你的 onCreateView() 方法如下所示:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return(inflater.inflate(R.layout.title_layout, container, false));
    }

而不是像这样:
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if(container == null){
            return null;                                
        }
        return(inflater.inflate(R.layout.title_layout, container, false));
    }

onCreateView()中提供的容器上不应使用Inflator.inflate,因为父容器会在onCreateView()完成后自动添加。相反,您应该传递null作为容器参数进行充气。 - Graeme
这很不直观。那么 container 的意义是什么? - Cheezmeister
@Graeme,false 的最后一个参数意味着您实际上不会将充气的视图附加到容器上,并且传递容器以便为充气的视图使用正确的 LayoutParam - Dandre Allison
我从未遇到过传递 null 无法正确填充其容器的情况? - Graeme

4

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