7

I've a problem with a Fragment that embeds the SuportMapFragment from the new Google Maps API. When my fragment is created it fetches some data in an AsyncTask beginning in the onResume method. While this happens the MapFragment remains off-screen, instead a progress bar is displayed.

When the AsyncTask is completed, I register with the onGlobalLayout event of the mapView. Then I display the fragment, which results in the map view being shown and layouted. onGlobalLayout is triggered and the animation starts.

This worked fine for quite some time, until I started optimizing the operations in the data gathering AsyncTask to complete almost instantaneously. Now the app will often crash when started, more often in Release than Debug, which is why I think that I'm dealing with a racing condition here that I'm not aware of.

The exception reads:

Map size should not be 0. Most likely, layout has not yet occured for the map view.

Any insights are greatly appreciated.

For reference: This is the code that seems to cause the crash:

// Hide status UI
this.progressBar.setVisibility(View.INVISIBLE);
this.statusTextView.setVisibility(View.INVISIBLE);

// Update state
this.uraDataDownloadFinished = true;

// Schedule map translation to occur when layout is complete
final View mapView = this.mapFragment.getView();
if (mapView.getViewTreeObserver().isAlive())
{
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
    {
        @SuppressWarnings("deprecation")
        @SuppressLint("NewApi") // We check which build version we are using.
        @Override
        public void onGlobalLayout()
        {
            Log.d(TAG, "onGlobalLayout()");
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
            {
              mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }
            else
            {
              mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }

            // here we crash
            this.map.animateCamera(
                    CameraUpdateFactory.newLatLngBounds(
                            LatLngBounds.builder()
                                .include(topLeft)
                                .include(topRight)
                                .include(bottomLeft)
                                .include(bottomRight)
                                .build(),
                            0),
                    durationMs,
                    null);
        }
   });
}

// Show map (which will eventually trigger onGlobalLayout)
this.getFragmentManager().beginTransaction().show(this.mapFragment).commit();
this.mapFragment.getMap().setOnCameraChangeListener(this);

Thanks a lot!

Chris
  • 3,192
  • 4
  • 30
  • 43

2 Answers2

5

I am not sure but as you are using AsyncTask is probably that you are trying to get map bounds before map has undergone layout.

You can see the documentation, it suggest to use newLatLngBounds(boundary, width, height, padding) instead .

jgonza73
  • 344
  • 1
  • 10
  • Thanks, that works. Since the map completely fills a layout container, I can use its dimensions instead. However, my question remains. Since I'm waiting for `onGlobalLayout()`, I should not run into this problem, should I? – Chris Jan 25 '13 at 13:47
  • No, I don't think so, you have to wait till maps has undergone layout. – jgonza73 Jan 26 '13 at 06:48
  • I see, so the `MapView`'s layout is no indication for the map layout. Just out of curiosity: Is there a way to listen to the layout process of the map or is that not possible? I'm not seeing anything obvious looking at the API. – Chris Jan 28 '13 at 10:26
  • 1
    I never use that, but you can figure it out following this [post](http://stackoverflow.com/questions/13692579/movecamera-with-cameraupdatefactory-newlatlngbounds-crashes) – jgonza73 Jan 28 '13 at 13:02
3

Try this one, it works for me:

map.setOnCameraChangeListener(new OnCameraChangeListener() {

    @Override
    public void onCameraChange(CameraPosition arg0) {
        // Move camera.
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));
        // Remove listener to prevent position reset on camera move.
        map.setOnCameraChangeListener(null);
    }
});
MOTIVECODEX
  • 2,624
  • 14
  • 43
  • 78
sonida
  • 4,411
  • 1
  • 38
  • 40