Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing fragments and orientation change

I'm developing an Android application targeting 2.x and 3.0 devices and thus I'm using the compatibilty API. I'm testing on Android 2.0.

I'm trying to replace a displayed fragment with another one (search form with search results) and I'm experiencing a crash on orientation change when the second (results) fragment is displayed.

Basically, I have an activity, that includes a fragment defined in layout xml as

    <fragment class="org.prevoz.android.search.SearchFormFragment"
              android:id = "@+id/search_form_fragment"
              android:layout_width = "fill_parent"
              android:layout_height = "fill_parent" />

The first fragment (SearchFormFragment) is displayed as a default. When user taps a "search" button, I replace the SearchFormFragment with SearchResultsFragment, which runs an AsyncTask (that's why I want to retain it) with

    // 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();

However, when the SearchFormFragment is displayed and orientation is changed, my application crashes with

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

The relevant onCreateView code from SearchFormFragment does get called and I return a valid view:

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

The crash only happens if the SearchResultsFragment is shown, the change works fine if the default SearchFormFragment is displayed.

So how do I retain the second fragment state through the orientation change? The documentation on API and state changes is really lacking.

like image 661
Mavrik Avatar asked Apr 02 '11 18:04

Mavrik


People also ask

How do I change the orientation of a fragment?

Use the following code line in the fragment where you want a specific (in this case portrait) orientation. getActivity(). setRequestedOrientation( ActivityInfo. SCREEN_ORIENTATION_PORTRAIT);

What happens when screen orientation changes in Android?

When you rotate your device and the screen changes orientation, Android usually destroys your application's existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.

How do I change fragments?

Use replace() to replace an existing fragment in a container with an instance of a new fragment class that you provide. Calling replace() is equivalent to calling remove() with a fragment in a container and adding a new fragment to that same container. transaction. commit();

Can fragments be reused?

Understanding Fragments Using the support library, fragments are supported back to all relevant Android versions. Fragments encapsulate views and logic so that it is easier to reuse within activities. Fragments are standalone components that can contain views, events and logic.


2 Answers

Don't create the SearchFormFragment in XML. Instead have an empty FrameLayout which you populate in Activity.onCreate() without adding it to the back stack. This way the Activity will keep the current Fragment instead of trying to add the one specified in XML.

Also, using AsyncLoader may be a better approach, see http://code.google.com/p/android/issues/detail?id=14944

like image 112
alexanderblom Avatar answered Oct 17 '22 12:10

alexanderblom


After struggling with this for many hours, I finally got it. The problem is not in the setup or the way we call Fragment class at all. It has to do with a wrong message being displayed on the screen. If you have a check for a container being null in your onCreateView() in your Ftagment class, you will get that "unable to inflate fragment" message, instead of container is null message. So, do not check for null container in your onCreateView(). So make sure your onCreateView() looks like this:

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

and NOT like this:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if(container == null){
            return null;                                
        }
        return(inflater.inflate(R.layout.title_layout, container, false));
    }
like image 36
Henry Avatar answered Oct 17 '22 11:10

Henry