22

I do not want to use or extend SupportMapFragment or MapFragment. I have my own base class with a bunch of code in it.

The documentation clearly states that when someone uses MapView by itself, all corresponding lifecycle methods (onCreate() onResume() etc.) should be called.

Most of the lifecycle methods in a Fragment are similar to an Activity but when I switch back and forth between my Fragment I eventually get an obfuscated NPE in onDestroy() or in onResume() methods.

All the samples provided use an Activity with a MapView but not a custom Fragment.

Has someone done that already? Can you provide sample code of a MapView in your own Fragmentclass?

dnkoutso
  • 6,041
  • 4
  • 37
  • 58

3 Answers3

35

I struggled a bit with PoPy's answer but in the end I managed it and here is what I came up with. Probably this is helpful to others which will also come across this issue:

public class MyMapFragment extends Fragment {

    private MapView mMapView;
    private GoogleMap mMap;
    private Bundle mBundle;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View inflatedView = inflater.inflate(R.layout.map_fragment, container, false);

        try {
            MapsInitializer.initialize(getActivity());
        } catch (GooglePlayServicesNotAvailableException e) {
            // TODO handle this situation
        }

        mMapView = (MapView) inflatedView.findViewById(R.id.map);
        mMapView.onCreate(mBundle);
        setUpMapIfNeeded(inflatedView);

        return inflatedView;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBundle = savedInstanceState;
    }

    private void setUpMapIfNeeded(View inflatedView) {
        if (mMap == null) {
            mMap = ((MapView) inflatedView.findViewById(R.id.map)).getMap();
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
    }

    @Override
    public void onResume() {
        super.onResume();
        mMapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mMapView.onPause();
    }

    @Override
    public void onDestroy() {
        mMapView.onDestroy();
        super.onDestroy();
    }
}

And here is the corresponding res/layout/map_fragment.xml:

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

    <com.google.android.gms.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

You can omit the RelativeLayout (and move the xmlns declartion up to your new root element, in this case the com.google.android.gms.maps.MapView) if you have just one element in your layout like in this example.

Jens Kohl
  • 5,899
  • 11
  • 48
  • 77
  • 1
    Thanks for your solution, it helped me. However, are you using this in a view pager ? Because I have a problem with this solution and a view pager. I don't know how to keep map state (see my comment on PoPy's answer for more details). Could you help me ? – Sébastien BATEZAT Mar 15 '13 at 19:16
  • @jens thanx for the code, it works for the first fragment of my pager but when I swipe to 2nd fragment, the MapView shows nothing - it's like there was nothing in that frame. FYI, the fragment transactions are done in the parent fragment's `onCreateView` method. Another issue I have found - I have implemented `setOnMapClickListener` for the GoogleMap object but it seems all the clicks are not handled properly. It gets some of the clicks, not all. Don't know what happens here. – Khobaib Apr 10 '13 at 23:07
  • Update - It seems that we have to call with `getChildFragmentManager()` from the parent fragment instead of `getFragmentManager()` to avoid the issue that map comes in only one fragment. But while swipe in pager, I am getting some **NullPointerException**. – Khobaib Apr 10 '13 at 23:35
  • 2
    Thanks for your implementation. I've noticed one issue. When restoring this map fragment from backstack, map camera position is lost. In my current implementation I use OnCameraChangeListener to save camera position each time it changes in fragment instance variable. When restoring fragment from backstack, I check this instance variable and if it is not null, move map camera to this saved position. – pvshnik Oct 30 '13 at 08:34
  • Then how to add/use this MyMapFragment in my Activity? Can you please provide the complete example? I want to add mapview dynamically and moreover want to set width and height of mapview based on screen width and height. Please guide me for this.. – YuDroid May 19 '14 at 07:20
  • @YuDroid You should create a new question here on SO for that. Because that's out of scope of the OP. – Jens Kohl May 19 '14 at 07:36
  • @pvshnik What callback do you use for restoring fragment from backstack, please? – Marcel Bro Aug 11 '14 at 10:21
  • 1
    I had to call `MapView.onDestroy()` and clear the `MapView` reference inside `Fragment.onDestroyView()` or else the resumed fragment was showing an empty gray map (kinda make sense since calling `MapView.onCreate()` inside `Fragment.onCreateView()`). – mmathieum Nov 27 '14 at 17:16
13

I succeeded in including a MapView (v2) in a custom Fragment itself embedded in a ViewPager. In my case, the MapView is included in the Fragment layout file. I have had to call lifecycle methods on MapView (onCreate() called onCreateView() from the Fragment), and to call manually MapsInitializer.initialize(context) to avoid a NullPointerException from class BitmapDescriptorFactory (to get the bitmap for markers). This last trick is strange, and I don't know why the Map system isn't properly initialized itself without this call, maybe it's just a bug in the current version ...

In my case I haven't had any NullPointerException in onResume() or onDestroy().

Matt Seymour
  • 8,880
  • 7
  • 60
  • 101
PoPy
  • 146
  • 1
  • 3
  • Here are some details about MapsInitializer.initialize(context) from the official documentation: [BitmapDescriptorFactory](http://developer.android.com/reference/com/google/android/gms/maps/model/BitmapDescriptorFactory.html) [MapsInitializer](http://developer.android.com/reference/com/google/android/gms/maps/MapsInitializer.html#initialize(android.content.Context)) – PoPy Dec 11 '12 at 22:24
  • 1
    clear I do have a Map object already but for some reason the exception is still thrown. It ended up working when I added this static ugly call. – dnkoutso Dec 13 '12 at 19:32
  • @PoPy I'm trying to do the same as yourself (i.e : including mapView in a viewPager), but how do you do to keep mapView state (markers, position, zoom, ...). My state is lost when I go from page 1 (with map) to page 3, and return to page 1 ! – Sébastien BATEZAT Mar 15 '13 at 13:13
  • @Sebastien I haven't tried to save the state for the moment, so sorry but i can't help you on this – PoPy Apr 11 '13 at 14:58
  • @Sebastien I believe MapView.onSaveInstaceState method is for the problem you are having. – Pijusn Jun 05 '14 at 10:23
-2

While using a separate MapView following two things are of vital importance

    //at Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      MapsInitializer.initialize(this);
      mapView.onCreate(savedInstanceState); 
    }
    //or at Fragment
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                       Bundle savedInstanceState) {

       MapsInitializer.initialize(getActivity());  
       mapView.onCreate(mBundle);
     }
    //along with the following
    @Override
    protected void onResume() {
       super.onResume();
       if (mapView != null)
        mapView.onResume();
      }

    @Override
    protected void onDestroy() {
       super.onDestroy();
       if (mapView != null)
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
      super.onLowMemory();
      if (mapView != null)
        mapView.onLowMemory();
    }
laaptu
  • 2,963
  • 5
  • 30
  • 49