30

In the onCreate method, I am making use of the SupportMapFragment to show a map.

    SupportMapFragment fragment = new SupportMapFragment();
    getSupportFragmentManager().beginTransaction()
            .add(android.R.id.content, fragment).commit();

In conjunction to this, I would like to add a marker. The problem is when the call to getMap is null, when can I try again? Is there an event I can register for or is my approach in and of itself wrong?

    mMap = ((SupportMapFragment)(getSupportFragmentManager().findFragmentById(R.id.map))).getMap();
    if(mMap == null)
        //what do I do here?

The map is in fact displaying on the phone however I appear to be having no luck in obtaining the reference to add markers.

UPDATE:

The reason I was creating the SupportMapFragment via the constructor is because the typical setContentView was crashing and did not work. This put me in the predicament where I could not obtain my reference in the onCreate method since I was in fact creating the SupportMapFragment at that time. In further investigation, it appears my setContentView issue was a byproduct of not having both the Google-play-services jar AND the module/src set up as part of the overall project. Upon doing BOTH of these, setContentView now works and I can obtain the reference via getMap() as I would expect.

lots.xml...

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:name="com.google.android.gms.maps.SupportMapFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

LotsActivity.java...

public class LotsActivity extends FragmentActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.lots);

        GoogleMap mMap;
        mMap = ((SupportMapFragment)(getSupportFragmentManager().findFragmentById(R.id.map))).getMap();
        if(mMap == null)
            //this should not occur now
    }
Ranjit
  • 5,130
  • 3
  • 30
  • 66
Aaron McIver
  • 24,527
  • 5
  • 59
  • 88
  • Mclver you should move your solution to an answer and then mark it as such rather than having it as an edit inside the question. – Adam Dec 17 '13 at 07:58

7 Answers7

35

EDIT: getMap is deprecated now

The problem is when the call to getMap is null, when can I try again?

That depends upon the nature of the problem.

If you set up the SupportMapFragment via the <fragment> element in the layout, you can call getMap() successfully in onCreate(). But, if you create the SupportMapFragment via the constructor, that's too soon -- the GoogleMap does not yet exist. You can extend SupportMapFragment and override onActivityCreated(), as getMap() is ready by then.

However, getMap() can also return null for a bigger problem, such as Google Play Services not being installed. You would need to use something like GooglePlayServicesUtil.isGooglePlayServicesAvailable() to detect this condition and deal with it however you wish.

Marian Paździoch
  • 8,813
  • 10
  • 58
  • 103
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • This makes sense and led me to further investigate why `setContentView` was not working. In solving that, I also solved the issue of obtaining a null reference to the `GoogleMap` object, which your insight helped me achieve. – Aaron McIver Dec 27 '12 at 05:54
  • 1
    @CommonsWare where is *"and override onActivityCreated(), as getMap() is ready by then."* documented? I see still `null` in `onActivityCreated()` in my current project – Marcin Orlowski Jan 02 '13 at 01:49
  • @WebnetMobile.com: "documented?" -- a fragment's views are not created until its transaction gets committed, and so it's "documented" in terms of general fragment behavior. `onActivityCreated()` is usually a good post-view-creation lifecycle method. "I see still null in onActivityCreated() in my current project" -- I cannot explain that. – CommonsWare Jan 02 '13 at 12:03
  • 2
    As I already said - this method does not work for me. `getMap()` always returns `null` here, despite the fact map is visible. So for a record, what works for me, is extending `SupportMapFragment` and using this instead. I set up map in my subclass `onCreateView()`. No clue what is the real culprit though. – Marcin Orlowski Jan 18 '13 at 01:55
  • There is guidance on how to deal with this on the client: http://developer.android.com/google/play-services/setup.html#ensure Basically, if you handle the result, you can display a dialog and prompt the user to install them. – Chuck D Apr 19 '13 at 01:01
  • @CommonsWare You are so rock!! Thank you for your great information! – cmcromance May 29 '13 at 10:14
  • Still it not works. In my application the map always returns null. can anyone explain me how they succeeded, so i can able to add markers through the map object. – Ranjit Aug 21 '13 at 12:36
14

I ended up extending the SupportMapFragment class and using a callback. The code is here:

public class MySupportMapFragment extends SupportMapFragment {

public MapViewCreatedListener itsMapViewCreatedListener;

// Callback for results

public abstract static class MapViewCreatedListener {
    public abstract void onMapCreated();
}

@Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = super.onCreateView(inflater, container, savedInstanceState);
    // Notify the view has been created
    if( itsMapViewCreatedListener != null ) {
        itsMapViewCreatedListener.onMapCreated();
    }
    return view;
}
}

I would rather have used an interface and override the onAttach(activity) method, but in my case, I didn't want the callback to go back to my MainActivity. I wanted it to return to an instance of Fragment. (The GoogleMap was essentially a fragment inside a fragment) I setup the callback and programmatically loaded the map with this. I would like to have set itsMapViewCreatedListener inside the constructor of MySupportMapFragment, but using anything other than parameterless constructors is discouraged.

itsMySupportMapFragment = new MySupportMapFragment();
MapViewCreatedListener mapViewCreatedListener = new MapViewCreatedListener() {
    @Override
    public void onMapCreated() {
        initPlacesGoogleMapCtrl();
    }
};
itsMySupportMapFragment.itsMapViewCreatedListener = mapViewCreatedListener;
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.mapFragmentContainer, itsMySupportMapFragment);
transaction.addToBackStack(null);
transaction.commit();

And then, when I got the call back, I could get the map; no more null!

public void initPlacesGoogleMapCtrl() {
    // Map ready, get it.
    GoogleMap googleMap = itsMySupportMapFragment.getMap();
    // Do what you want...
}
strangeluck
  • 846
  • 9
  • 10
  • Why did you bother to extend a class and create a callback...why just don't extend it in place and make it an anonimous class, just like you did with the listener....sounds a lot simpler to me, unless you pretend to have multiple of these to use. – Michel Feinstein Jul 10 '14 at 21:53
11

I would comment on CommonsWare's answer but I don't have enough rep for that. Anyways, I was also having this problem that getMap() would return null in onActivityCreated. My setup is this: I have MainActivity that contains a fragment. In the onCreateView method of that fragment I created the SupportMapFragment and then added it to the fragment via the childFragmentManager. I kept reference to the SupportMapFragment and expected it to give me the map in onActivityCreated which it did not. The solution to that problem was to override the onActivityCreated of the SupportMapFragment but not the parent fragment. I did it like this in onCreateView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_location, container, false);
    mMapFragment = new SupportMapFragment() {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            mMap = mMapFragment.getMap();
            if (mMap != null) {
                setupMap();
            }
        }
    };
    getChildFragmentManager().beginTransaction().add(R.id.framelayout_location_container, mMapFragment).commit();
return v;   
}
hordurh
  • 111
  • 2
  • 1
    One should not create non-static inner fragment because compiler will automatically create constructor to provide outer class to the fragment as an argument. Then on fragment recreation you will get `InstantiationException` because there is no default empty constructor for the fragment. – Alexander Mironov Jun 02 '13 at 06:51
  • This is just what I'd been searching for for hours and hours, thanks! – barca_d May 02 '14 at 14:38
7

Last month (December 2014) Google gave official solution for this problem. In newest (6.5) Google Play Services :

The getMap() method in MapView and MapFragment is now deprecated in favor of the new getMapAsync() method.

With getMapAsync(OnMapReadyCallback callback) you can set up listener for when GoogleMap is ready. It is passed in callback and it is provided to be not-null.

Google provided sample code for this:

public class MainActivity extends FragmentActivity
    implements OnMapReadyCallback {
...
}

and when initializing fragment:

MapFragment mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);

when map is ready, you callback will be called

@Override
public void onMapReady(GoogleMap map) {
    map.addMarker(new MarkerOptions()
        .position(new LatLng(0, 0))
        .title("Marker"));
}
michalbrz
  • 3,354
  • 1
  • 30
  • 41
0

You can also use MapsInitializer.initialize(context); if the problem is related to the libraries not being initialized.

theelfismike
  • 1,621
  • 12
  • 18
  • This does not solve the problem a null map when SupportMapFragment is instantiated with "new" and added to a view with a fragmentTransaction – Glenn Bech Aug 03 '13 at 20:44
  • 2
    If you need to use the view immediately, you can call fragmentManager.executePendingTransactions() which will make the commit happen immediately. – theelfismike Aug 05 '13 at 01:26
0

With the latest GoogleServices you can use MapFragment.getMapAsync. Directly from the docs

Sets a callback object which will be triggered when the GoogleMap instance is ready to be used.

Mojo Risin
  • 8,136
  • 5
  • 45
  • 58
0

if map == null then find the map

 if (mGoogleMap == null)
    {
        SupportMapFragment mapFragment1 = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);

        mapFragment1.getMapAsync(this);
    }

and then call your function onMapReady();

Mahesh Gawhane
  • 348
  • 3
  • 20