Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does GoogleMap.animateCamera() crash after onGlobalLayout()?

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!

like image 954
Chris Avatar asked Jan 22 '13 10:01

Chris


2 Answers

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 .

like image 157
jgonza73 Avatar answered Sep 27 '22 19:09

jgonza73


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);
    }
});
like image 42
sonida Avatar answered Sep 27 '22 19:09

sonida