14

The current Android Google Maps API requires you call mapFragment.getMapAsync with a OnMapReadyCallback before you can access the GoogleMap. I assumed that once you had the GoogleMap it would then be safe to call moveCamera() but I was seeing crash reports with an IllegalStateException which said Map size can't be 0. Most likely, layout has not yet occured for the map view.

So I tried adding a ViewTreeObserver.OnPreDrawListener, and moving the moveCamera() call to the onPreDraw method, as the docs for that say "At this point, all views in the tree have been measured and given a frame". But I still see some crash reports with the same problem. I can't find any documentation for this — there's questions like moveCamera with CameraUpdateFactory.newLatLngBounds crashes but they pre-date the getMapAsync API, so they're not much help.

Community
  • 1
  • 1
Jonathan Caryl
  • 1,330
  • 3
  • 12
  • 30
  • I'm seeing this too - strangely, it only seems to affect camera movements to `CameraUpdateFactory.newLatLngBounds(bounds, padding)`, but not `CameraUpdateFactory.newLatLng(latLng)` - so I'm assuming it has something to do with the padding. I can replicate when creating new map activity from the template in Android Studio on a LG G4 w/ Android 5.1. – Sean Barbeau Aug 31 '15 at 14:58

3 Answers3

11

I had the same issue. This google maps library throws the exception when an app try to change the camera with this camera update until the map has undergone layout (in order for this method to correctly determine the appropriate bounding box and zoom level, the map must have a size). It described here.

It's my solution:

@Override
public void onMapReady(GoogleMap googleMap) {
    googleMap.setOnMapLoadedCallback(this);
}

@Override
public void onMapLoaded() {
    googleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(…));
}
mbelsky
  • 6,093
  • 2
  • 26
  • 34
  • 1
    The docs here https://developers.google.com/android/reference/com/google/android/gms/maps/OnMapReadyCallback explicitly state to not chain the events. "Do not chain the OnMapReadyCallback and OnGlobalLayoutListener listeners, but instead register and wait for both callbacks independently, since the callbacks can be fired in any order." – m0bl Jun 07 '17 at 12:48
0

You can do anything you want right after you get onMapReady(GoogleMap googleMap) called.

This is working right now in mi app:

mapFragment.getMapAsync(new OnMapReadyCallback() {

            @Override
            public void onMapReady(GoogleMap googleMap) {

                map = googleMap;
                map.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(usr.getLatitud(), usr.getLongitud()), 14.6f));
            }
});
Nanoc
  • 2,381
  • 1
  • 20
  • 35
  • 1
    That may work in the general case, but I've tried that and seen crash reports come in. I don't know if they're related to a particular version of Android, or a particular device, but they certainly happen. – Jonathan Caryl Jun 19 '15 at 10:49
  • Thats working fine from API 14 to 21, are you sure that exception is caused by this? – Nanoc Jun 19 '15 at 11:06
  • Yes, yes I am. If you re-read the question, you'll see I already tried what you suggest, but I saw crashes. Not hundreds of crashes, but a few. With my updated approach I see even fewer crashes, but the problem isn't completely solved. – Jonathan Caryl Jun 19 '15 at 12:30
  • I got the same problem. On some samsung devices the map seems to be not ready when the onMapReady method is called... – Stephan Huewe Oct 31 '16 at 09:36
0

I had the same problem. I adapted a solution from the link you provided, which seems to work for me. In onCreate():

rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    public void onGlobalLayout() {displayMap();}
});

OnGlobalLayoutListener() seems to be called multiple times per layout, so perhaps a more precisely targetted listener would be better. To avoid the inefficiency of manipulating the map more than necessary, I used a boolean mMapInitialised.

onMapReady():

@Override public void onMapReady(GoogleMap map) {
    mMap =  map;
    displayMap();   // if width of MapFragment is known
}

displayMap():

void displayMap() {
    // Check that mMap has been initialised AND the width of the MapFragment has been set:
    if (mMapInitialised || mMap==null) return;
    if (mMapFragment.getView().getRight() <= 0) return;
    LatLngBounds bounds = new LatLngBounds(new LatLng(mLat_min,mLng_min), new LatLng(mLat_max,mLng_max));
    CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 10);
    mMap.moveCamera(cameraUpdate);
    mMapInitialised = true;
}
Peter McLennan
  • 371
  • 4
  • 11
  • I'm glad that I'm not the only person seeing the issue. I wonder in your solution whether you should remove the `OnGlobalLayoutListener` once you've successfully initialised the map? – Jonathan Caryl Jul 20 '15 at 09:16
  • Good question! There's probably a small efficiency gain to be had by doing so. Note that leaving it won't result in further attempts to set up the map because of the mapInitialised flag. It does raise the question about what happens if a subsequent layout change varies the size of the MapFragment, but the normal fragment plumbing seems to handle that okay. – Peter McLennan Jul 20 '15 at 20:39
  • It should be safe to remove the listener. After the camera position is first setup, let it be handled by the MapView (remembers location on screen rotation, even if the user has moved the map elsewhere). On screen rotation you'll most certainly be recreating the activity/view hierarchy, so you'll need a new layout listener anyway. – Eugen Pechanec Dec 06 '15 at 12:25