27
votes

I'm trying make an app that I'm building take a search term from the main activity, return results, and then have the results be clickable such that a detail could be viewed from each results. I'm doing this by using a MainActivity, a ResultsActivity, and PlaceActivity and then a ListFragmentClickable (which extends ListFragment). If the handset is oriented in portrait mode, the results list should be viewable, with the details being viewed only if a result is clicked. If the handset is landscape, a detail window should pop up to the right of the list when an item is selected.

At runtime, I get an error that reads "error inflating class fragment". I have no idea what is causing it, and I'd love help in getting rid of it.

My ListFragmentClickables are called by my ResultsActivity, which is here:

public class ResultsActivity extends FragmentActivity implements ListFragmentClickable.OnItemSelectedListener{

private ArrayAdapter<String> mAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_results_view);

    //Receive searchTerm from MainActivity
    Intent intent = getIntent();
    String searchTerm = intent.getStringExtra(MainActivity.SEARCH_TERM);

    mAdapter = new ArrayAdapter<String>(this, R.layout.item_label_list);

    FragmentManager     fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();

    FactualResponderFragment responder = (FactualResponderFragment) fm.findFragmentByTag("RESTResponder");
    if (responder == null) {
        responder = new FactualResponderFragment();
        ft.add(responder, "RESTResponder");
    }

    Bundle bundle = new Bundle();
    bundle.putString("search_term", searchTerm);
    responder.setArguments(bundle);

    ft.commit();
}

public ArrayAdapter<String> getArrayAdapter() {
    return mAdapter;
}

@Override //creates a DetailFragment when a list item is selected
public void onItemSelected(String link) {
    DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.detailFragment);
    if (fragment != null && fragment.isInLayout()) {
          fragment.setText(link);
    } else {
      Intent intent = new Intent(getApplicationContext(), PlaceActivity.class);
          intent.putExtra(PlaceActivity.EXTRA_URL, link);
      startActivity(intent);
    }
}

}

Here's the ListFragmentClickable class:

public class ListFragmentClickable extends ListFragment{

  private OnItemSelectedListener listener;


  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
      View view = inflater.inflate(R.layout.activity_results_view, container, false);
      return view;
  }

  @Override
      public void onListItemClick(ListView l, View v, int position, long id) {
          updateDetail(); //see bottom
      }

  public interface OnItemSelectedListener {
      public void onItemSelected(String link);
  }

  @Override
  public void onAttach(Activity activity) {
      super.onAttach(activity);
      if (activity instanceof OnItemSelectedListener) {
          listener = (OnItemSelectedListener) activity;
      } else {
          throw new ClassCastException(activity.toString()
            + " must implemenet ListFragmentClickable.OnItemSelectedListener");
      }
  }


 public void updateDetail() {
        // Create fake data
    String newTime = String.valueOf(System.currentTimeMillis());
    // Send data to Activity
    listener.onItemSelected(newTime); //should direct PlaceActivity!!
  }
}

Here's my layout file for the ResultsActivity (activity_results_activity.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

<fragment
    android:id="@+id/fragment_content"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    android:layout_marginTop="?android:attr/actionBarSize"
    class="com.example.blobtag2.ListFragmentClickable" ></fragment>


<ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:divider="#b5b5b5"
    android:dividerHeight="1dp"></ListView>

</LinearLayout> 

And finally here's the log:

03-21 22:32:03.297: E/AndroidRuntime(764): FATAL EXCEPTION: main
03-21 22:32:03.297: E/AndroidRuntime(764): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.blobtag2/com.example.blobtag2.ResultsActivity}: android.view.InflateException: Binary XML file line #7: Error inflating class fragment
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread.access$600(ActivityThread.java:130)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.os.Handler.dispatchMessage(Handler.java:99)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.os.Looper.loop(Looper.java:137)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread.main(ActivityThread.java:4745)
03-21 22:32:03.297: E/AndroidRuntime(764):  at java.lang.reflect.Method.invokeNative(Native Method)
03-21 22:32:03.297: E/AndroidRuntime(764):  at java.lang.reflect.Method.invoke(Method.java:511)
03-21 22:32:03.297: E/AndroidRuntime(764):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
03-21 22:32:03.297: E/AndroidRuntime(764):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
03-21 22:32:03.297: E/AndroidRuntime(764):  at dalvik.system.NativeStart.main(Native Method)
03-21 22:32:03.297: E/AndroidRuntime(764): Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class fragment
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
03-21 22:32:03.297: E/AndroidRuntime(764):  at com.example.blobtag2.ListFragmentClickable.onCreateView(ListFragmentClickable.java:32)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:846)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1061)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1160)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:272)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
03-21 22:32:03.297: E/AndroidRuntime(764):  at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:256)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.Activity.setContentView(Activity.java:1867)
03-21 22:32:03.297: E/AndroidRuntime(764):  at com.example.blobtag2.ResultsActivity.onCreate(ResultsActivity.java:37)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.Activity.performCreate(Activity.java:5008)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
03-21 22:32:03.297: E/AndroidRuntime(764):  ... 11 more
03-21 22:32:03.297: E/AndroidRuntime(764): Caused by: java.lang.IllegalArgumentException: Binary XML file line #7: Duplicate id 0x7f070003, tag null, or parent id 0x0 with another fragment for com.example.blobtag2.ListFragmentClickable
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:277)
03-21 22:32:03.297: E/AndroidRuntime(764):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
03-21 22:32:03.297: E/AndroidRuntime(764):  ... 30 more

Any and all help is greatly, greatly appreciated. Please let me know if there's anything else I can add?

7

7 Answers

40
votes

The problem is the fragment on xml is loaded twice, and the second time it is added to the FragmentManager you get a IllegalArgumentException. I got the same problem yesterday.

My solution, I change it to re-create the fragment dynamically instead of define it on the xml file:

Change this xml file part:

<fragment
    android:id="@+id/fragment_content"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    android:layout_marginTop="?android:attr/actionBarSize"
    class="com.example.blobtag2.ListFragmentClickable" ></fragment>

To this instead:

<FrameLayout
    android:id="@+id/fragment_content"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="match_parent"
    android:layout_marginTop="?android:attr/actionBarSize" />

An then in the onCreate, yo should replace FrameLayout for a new fragment:

Fragment fragment = new ListFragmentClickable();
FragmentManager     fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_content, fragment);
ft.commit(); 

And then you have the same but, you won't get a Duplicate ID error.

38
votes

It occurs when fragments are defined in XML (statically): FragmentManager doesn't manage child fragments if parent fragment is destroyed. Then, it breaks ("duplicate id" error) when the XML is inflated for the second time.

I bypass this problem removing the XML fragment manually when parent is destroyed, with this code in the parent fragment:

@Override
public void onDestroyView() {

    FragmentManager fm = getFragmentManager();

    Fragment xmlFragment = fm.findFragmentById(R.id.XML_FRAGMENT_ID);
    if (xmlFragment != null) {
        fm.beginTransaction().remove(xmlFragment).commit();
    }

    super.onDestroyView();
}

Note for copypasters: XML_FRAGMENT_ID is the id of the fragment in the XML ;)


Furthermore, I prefer a new class that wraps XML fragments. It simplyfies the code since you just need to extend your fragment class from it. Add this class to your project:

package net.treboada.mytests.fragments;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.util.AttributeSet;

public class XmlFragment extends Fragment {

    @Override
    public void onInflate(Activity activity, AttributeSet attrs,
            Bundle savedInstanceState) {

        FragmentManager fm = getFragmentManager();
        if (fm != null) {
            fm.beginTransaction().remove(this).commit();
        }

        super.onInflate(activity, attrs, savedInstanceState);
    }
}

Then, extend your XML fragment classes:

package net.treboada.mytests.fragments;

public class TestXmlFragment01 extends XmlFragment {

    // ...

}

and voilà! :)

11
votes

Let's try this out. I got the same problem and i fixed my problem using this code.

 View root;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (root!= null) {
        ViewGroup parent = (ViewGroup) root.getParent();
        if (parent != null)
            parent.removeView(root);
    }
    try {
        root= inflater.inflate(R.layout.map_layout, container, false);
    } catch (InflateException e) {
        /* map is already there, just return view as it is */
    }
    return root;
}
2
votes

If you were reading the other answers here feeling that they seemed correct, look here https://stackoverflow.com/a/19815266/1139784 because it references the documentation explaining how this is not supported (at least in my case, it is hard to tell from this question whether the layout fragment nesting is happening)

Note: You cannot inflate a layout into a fragment when that layout includes a <fragment>.
Nested fragments are only supported when added to a fragment dynamically.

More data here Best practice for nested fragments in Android 4.0, 4.1 (<4.2) without using the support library

Edit: Also, if you're considering using nested fragments and don't need the lifecycle management, you can create a custom view group (like extending LinearLayout) instead. http://www.vogella.com/tutorials/AndroidCustomViews/article.html

2
votes

You can extend you fragment (which you load in xml) from XmlFragment. It handles parent FragmentManager and removes itself.

public class XmlFragment extends BaseFragment {

    @Override
    public void onDestroyView() {
        Fragment parentFragment = getParentFragment();
        FragmentManager manager;
        if (parentFragment != null) {
            // If parent is another fragment, then this fragment is nested
            manager = parentFragment.getChildFragmentManager();
        } else {
            // This fragment is placed into activity
            manager = getActivity().getSupportFragmentManager();
        }
        manager.beginTransaction().remove(this).commitAllowingStateLoss();
        super.onDestroyView();
    }
}
1
votes

I had the same problem with fragment called twice and on the second time it crashed. Solution is to implement the remove statement in onDetach method:

    @override
public void onDetach(){
super.onDetach();
FragmentManager fm = getFragmentManager()

Fragment xmlFragment = fm.findFragmentById(R.id.yourfragmentid);
if(xmlFragment != null){
    fm.beginTransaction().remove(xmlFragment).commit();
    }
}
0
votes

This line

03-21 22:32:03.297: E/AndroidRuntime(764): Caused by: java.lang.IllegalArgumentException: Binary XML file line #7: Duplicate id 0x7f070003, tag null, or parent id 0x0 with another fragment for com.example.blobtag2.ListFragmentClickable

specifically the part at the end

Duplicate id 0x7f070003, tag null, or parent id 0x0 with another fragment for com.example.blobtag2.ListFragmentClickable

is telling you that at some point in the lifetime of your activity you're trying to create a view for ListFragmentClickable when one already exists. You need to figure out how this is occurring.