How do I know the map is ready to get used when using the SupportMapFragment?
Asked Answered
B

7

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
    }
Bonneau answered 26/12, 2012 at 22:52 Comment(1)
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.Tracy
T
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.

Tutelary answered 26/12, 2012 at 23:1 Comment(7)
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.Bonneau
@Tutelary where is "and override onActivityCreated(), as getMap() is ready by then." documented? I see still null in onActivityCreated() in my current projectKeratoid
@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.Tutelary
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.Keratoid
There is guidance on how to deal with this on the client: 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.Lotte
@Tutelary You are so rock!! Thank you for your great information!Vouch
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.Bedrock
L
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...
}
Latchkey answered 8/3, 2013 at 21:15 Comment(1)
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.Deceit
B
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;   
}
Birkle answered 19/2, 2013 at 11:56 Comment(2)
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.Woo
This is just what I'd been searching for for hours and hours, thanks!Jeffcott
P
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"));
}
Petrina answered 3/1, 2015 at 1:12 Comment(2)
so if you wanted an onLongClickListener, would it go in the onMapReady?Barvick
Yes, I'd do it this wayPetrina
B
0

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

Bors answered 2/1, 2013 at 22:38 Comment(2)
This does not solve the problem a null map when SupportMapFragment is instantiated with "new" and added to a view with a fragmentTransactionRanitta
If you need to use the view immediately, you can call fragmentManager.executePendingTransactions() which will make the commit happen immediately.Bors
O
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.

Outflow answered 31/12, 2014 at 16:44 Comment(0)
H
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();

Hardhearted answered 30/9, 2016 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.