Thursday 14 May 2015

Duplicate ID binary XML error in fragments

When dealing Google V2 maps in android tabs and Fragments which generally come across with a issue i.e Duplicate ID binary XML error.

To be more specific Error faced is below :
android.view.InflateException: Binary XML file line #7: Error inflating class fragment

Now why this happens :

Because you already have the fragment added in the FragmentManager, you can't add the same fragment twice which will cause duplication of ID i.e two fragments with same ID.

Now this generally happens when we are adding a fragment that contains a fragment in its layout as well, mostly in case of map fragments.

Our tendency is to add Mapfragment in XML layout like this :

<?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="vertical" >

    <fragment

        android:id="@+id/map"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        class="com.google.android.gms.maps.SupportMapFragment" />



</LinearLayout>


And our Fragment inflate this layout like this :

public class LocationFragment extends Fragment {



View mView;

private static GoogleMap map;

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

mView = inflater.inflate(R.layout.locationfragment, container, false);

return mView;

}

}


Now if we see closely what happening here. We will add this fragment in a tab, which contains a fragment in its layout as well.This is the reason of problem here.

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.

As per Andorid Documentation this approach is not recommended.We need to add map fragments Dynamically in order to accomplish nested fragments as per android guidelines.Check Nested fragments from below link :

http://developer.android.com/about/versions/android-4.2.html#NestedFragments

So Android-supported way is to add a fragment to another fragment is via a transaction from the child fragment manager as shown below :

XML Layout :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:map="http://schemas.android.com/apk/res-auto"

   android:layout_width="match_parent"

   android:layout_height="match_parent" >

<!-- Lots of fancy layout -->  
<RelativeLayout
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </RelativeLayout>
</RelativeLayout>


Java Code Snippet :

public class MyFragment extends Fragment {

private SupportMapFragment fragment;
private GoogleMap map;

@Override

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

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    FragmentManager fm = getChildFragmentManager();
    fragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
    if (fragment == null) {
        fragment = SupportMapFragment.newInstance();
       fm.beginTransaction().replace(R.id.map, fragment).commit();
    }
}



@Override
public void onResume() {
    super.onResume();
    if (map == null) {
        map = fragment.getMap();
        map.addMarker(new MarkerOptions().position(new LatLng(0, 0)));
    }
}
}

There are other ways to resolve the errors as well if you are willing to you map fragment inside a XML layout.Below are the ways to solve the error :

1. Remove fragment on onDestroyView() :

@Override
public void onDestroyView() {
    super.onDestroyView();
    MapFragment f = (MapFragment) getFragmentManager()
                                         .findFragmentById(R.id.map);

    if (f != null)
        getFragmentManager().beginTransaction().remove(f).commit();
}



2. Remove the parent view if it exists in onCreateView() :

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (view != null) {
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent != null)
            parent.removeView(view);
    }
    try {
        view = inflater.inflate(R.layout.map, container, false);
    } catch (InflateException e) {
    }
    return view;
}
The above two methods will resolve our issue of duplicate ID in map fragment when dealing with fragments and tabs, but its kind of "Jugaad"(tricky way) as we say in HINDI.So this might be working now but might not working in upcoming versions of android.So better way to go is as Android Recommends.


References : http://developer.android.com/about/versions/android-4.2.html#NestedFragments , www.stackoverflow.com.

Happy Coding!!
Cheers.

No comments:

Post a Comment