Nitin Mathur Nitin Mathur - 3 months ago 21
Android Question

Android custom view and nested fragments

I have a parent fragment in my view in which 2 child fragments are added programmatically to display maps. This is the layout for the child fragment, the map is rendered in FrameLayout.

location_field_view.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/settings_action_item_row_style"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="256dp"
android:id="@+id/map_fragment_container"
android:layout_marginTop="2dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is where map loads."/>
</FrameLayout>
</LinearLayout>


There is a click event for map to be loaded in corresponding FrameLayouts. My issue is, even though the FrameLayout view is getting inflated successfully (as I can see debug text "The map loads here" for both child fragments) but the map for 2nd child fragment renders in first child fragment itself (I see it getting refreshed when second map click listener gets triggered). What might be going wrong here?

Class to inflate above layout which is then added to parent fragment:

LocationField.java

public class LocationField extends LinearLayout
{
private MyMapFragment mMapFragment;
public LocationField()
{
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.location_field_view, this, true);
addMapFragment();
//mMapFragment.loadMap() method gets triggered based on a click event.
}

private void addMapFragment()
{
mMapFragment = MyMapFragment.newInstance(lat, long, marker);
FragmentTransaction transaction = mParentFragment.getChildFragmentManager().beginTransaction();
transaction.add(R.id.map_fragment_container, mMapFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}


MyMapFragment.java

public class MyMapFragment extends MapFragment implements OnMapReadyCallback
{
private static final String KEY_MARKER = "marker";
private static final String KEY_LATITUDE = "latitude";
private static final String KEY_LONGITUDE = "longitude";
private LatLng mLatLng;
private String mMarker;

public static MyMapFragment newInstance(Double latitude, Double longitude, String marker){

MyMapFragment myMapFragment = new MyMapFragment();
Bundle arguments = new Bundle();
arguments.putDouble(KEY_LATITUDE, latitude);
arguments.putDouble(KEY_LONGITUDE, longitude);
arguments.putString(KEY_MARKER, marker);
myMapFragment.setArguments(arguments);
return myMapFragment;
}

public void onCreate(Bundle savedInstance)
{
super.onCreate(savedInstance);
Bundle arguments = getArguments();
mLatLng = new LatLng(arguments.getDouble(KEY_LATITUDE), arguments.getDouble(KEY_LONGITUDE));
mMarker = arguments.getString(KEY_MARKER);
}

public void loadMap()
{
getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
if (googleMap == null) {
return;
}
googleMap.addMarker(new MarkerOptions().position(mLatLng).title(mMarker));
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mLatLng, 10f));
}
}

Answer

Solved the problem by setting id of FrameLayouts for map dynamically using method generateViewId().

Looks like FragmentManager was overwriting the frame IDs of previously added frames. (because they were all having same ID??)

Sharing the code changes required to solve the issue (in case someone else face this issue):

  1. Generate ID for frame layout dynamically. Initially there will be one id for frame layout in your view. Fetch the view object and assign newly generated view ID.

    mMapFrameId = View.generateViewId();

  2. Use the same ID during fragment transaction, while adding new fragment.

    mMapFragment = new MapFragment();
    FragmentTransaction transaction = mFragmentManager.beginTransaction();
    transaction.add(mMapFrameId, mMapFragment);
    transaction.addToBackStack(null);
    transaction.commit();