Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map Fragment couldn't be modified after Fragment Reload

  1. I have an activity with Navigation Drawer and HomeFragment.

  2. HomeFragment contains two nested fragments (child fragments): SupportMapFragment and ActionFragment.

I declare the GoogleMap object sMap as static in HomeFragment, so it can be accessed and modified by the nested fragment ActionFragment.

Everything works fine in the first run. The map can be manipulated from within the ActionFragment. But when the HomeFragment is removed and reloaded at a later point of time, the map could only be modified from the HomeFragment (parent fragment) and not from the ActionFragment (nested fragment) anymore.

I am failing to understand why something that works in the first instance doesn't work when the fragment is reloaded. Below are the codes which I have kept to minimum for easier understanding.


HomeFragment.java (Parent fragment)

public class HomeFragment extends Fragment {

    private static GoogleMap sMap;
    public static GoogleMap getMap() {
        return sMap;
    }


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


    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);

        // Obtain the GoogleMap object
        try {
            if (sMap == null) {
                sMap = ((SupportMapFragment) getActivity().getSupportFragmentManager()
                        .findFragmentById(R.id.home_map)).getMap();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Load child fragment
        if (savedInstanceState == null) {
            Fragment _actionFragment = new ActionFragment();
            FragmentTransaction _ft = getChildFragmentManager().beginTransaction();
            _ft.replace(R.id.action_container_bottom, _actionFragment);
            _ft.commit();
        }
    }


    @Override
    public void onDestroyView(){
        super.onDestroyView();

        // Remove the map fragment to prevent errors on the next load
        if(sMap != null){
            try {
                getActivity().getSupportFragmentManager()
                            .beginTransaction()
                            .remove(getActivity().getSupportFragmentManager()
                                    .findFragmentById(R.id.home_map))
                            .commit();

                sMap = null;
            } catch (Exception e){}
        }
    }
}

f_home.xml (Layout of parent fragment)

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

    <fragment
        android:id="@+id/home_map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/action_container_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" />

</RelativeLayout>

ActionFragment.java (Nested or Child fragment)

public class ActionFragment extends Fragment {
    private static GoogleMap sMap = HomeFragment.getMap();


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

        return inflater.inflate(R.layout.f_action, container, false);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);

        // check if map is created successfully or not
        if (sMap != null) {
            sMap.clear();
            sMap.setPadding(0, 0, 0, 300);
            sMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
            sMap.setBuildingsEnabled(false);
            sMap.setMyLocationEnabled(false);
            sMap.getUiSettings().setRotateGesturesEnabled(false);
            sMap.getUiSettings().setTiltGesturesEnabled(false);
            sMap.getUiSettings().setZoomControlsEnabled(false);
            sMap.moveCamera(CameraUpdateFactory.zoomTo(12));
        }
    }
}

Any help would be immensely appreciated as I am struggling with this for a long time. Thank you!

like image 816
Nirmal Avatar asked Dec 09 '22 05:12

Nirmal


1 Answers

Problem

The problem is that the classloader only loads ActionFragment once, that's why your static field is only initialized the first round.

What happens in first run:

1) HomeFragment#onActivityCreated()

  • HomeFragment#sMap is null -> locate via findViewById()
  • HomeFragment#sMap = [1]

2) classloader for ActionFragment

  • ActionFragment#sMap is null -> load from HomeFragment.getMap()
  • ActionFragment#sMap = [1]

... home gets destroyed ...

3) HomeActivity#onDestroyView()

  • HomeFragment#sMap = null

... home gets recreated ...

4) HomeFragment#onActivityCreated()

  • HomeFragment#sMap is null -> locate via findViewById()
  • HomeFragment#sMap = [2]

Since the class ActionFragment has already been loaded into memory, the class won't be loaded again and thus the code for your static field ActionFragment#sMap will not be executed again. it's still refering to the old (invalid) [1] instance.

Solution

In order to fix this issue, you can do the following:

public class ActionFragment extends Fragment {
    private GoogleMap mMap;

    public void onAttach(Activity activity) {
        super.onAttach(activity);  
        mMap = HomeFragment.getMap();
    }


   public void onDetach() { 
       super.onDetach() {
       mMap = null;
   }
}

With this code, each time the ActionFragment is attached, it will request the latest map-instance of HomeFragment.

like image 119
Flo Avatar answered Dec 31 '22 13:12

Flo