Замена фрагментов и изменение ориентации - PullRequest
19 голосов
/ 02 апреля 2011

Я разрабатываю приложение для Android, предназначенное для устройств 2.x и 3.0, и поэтому использую 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

Соответствующий код onCreateView из SearchFormFragment действительно вызывается, и я возвращаю правильное представление:

@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 голосов
/ 13 апреля 2011

Не создавайте SearchFormFragment в XML.Вместо этого есть пустой FrameLayout, который вы заполняете в Activity.onCreate(), не добавляя его в задний стек.Таким образом, Activity сохранит текущий Fragment вместо попытки добавить тот, который указан в XML.

Кроме того, использование AsyncLoader может быть лучшим подходом, см. http://code.google.com/p/android/issues/detail?id=14944

4 голосов
/ 24 сентября 2012

Для справочных целей: мы смогли решить очень похожую проблему, добавив android:id к нашему фрагменту, созданному в XML.

Это очень важное предварительное условие для поддержки безаварийного вращения - упоминается в качестве дополнительного примечания в документации :

Примечание. Для каждого фрагмента требуется уникальный идентификатор, который система может использовать для восстановления фрагмента при перезапуске действия (и который можно использоватьзахватить фрагмент для выполнения транзакций, например удалить его).

4 голосов
/ 04 июня 2011

Поработав с этим в течение многих часов, я наконец получил его. Проблема не в настройке или способе, которым мы называем класс Fragment вообще. Это связано с неправильным сообщением, отображаемым на экране. Если в вашем onCreateView () в вашем классе Ftagment есть проверка на нулевое значение контейнера, вы получите сообщение «неспособен накачать фрагмент», вместо контейнера - пустое сообщение. Поэтому не проверяйте наличие пустого контейнера в вашем 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));
    }
...