moveCamera with CameraUpdateFactory.newLatLngBound

2020-01-24 03:47发布

I'm making use of the new Android Google Maps API.

I create an activity which includes a MapFragment. In the activity onResume I set the markers into the GoogleMap object and then define a bounding box for the map which includes all of the markers.

This is using the following pseudo code:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
while(data) {
   LatLng latlng = getPosition();
   builder.include(latlng);
}
CameraUpdate cameraUpdate = CameraUpdateFactory
   .newLatLngBounds(builder.build(), 10);
map.moveCamera(cameraUpdate);

The call to map.moveCamera() causes my application to crash with the following stack:

Caused by: java.lang.IllegalStateException: 
    Map size should not be 0. Most likely, layout has not yet 

    at maps.am.r.b(Unknown Source)
    at maps.y.q.a(Unknown Source)
    at maps.y.au.a(Unknown Source)
    at maps.y.ae.moveCamera(Unknown Source)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub
        .onTransact(IGoogleMapDelegate.java:83)
    at android.os.Binder.transact(Binder.java:310)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a
        .moveCamera(Unknown Source)
    at com.google.android.gms.maps.GoogleMap.moveCamera(Unknown Source)
    at ShowMapActivity.drawMapMarkers(ShowMapActivity.java:91)
    at ShowMapActivity.onResume(ShowMapActivity.java:58)
    at android.app.Instrumentation
        .callActivityOnResume(Instrumentation.java:1185)
    at android.app.Activity.performResume(Activity.java:5182)
    at android.app.ActivityThread
        .performResumeActivity(ActivityThread.java:2732)

If - instead of the newLatLngBounds() factory method I use newLatLngZoom() method then the same trap does not occur.

Is the onResume the best place to draw the markers onto the GoogleMap object or should I be drawing the markers and setting the camera position somewhere else?

19条回答
Luminary・发光体
2楼-- · 2020-01-24 04:17

The accepted answer wouldn't work for me because I had to use the same code in various places of my app.

Instead of waiting the camera to change, I created a simple solution based on Lee's suggestion of specifying the map size. This is in case your map is the size of the screen.

// Gets screen size
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// Calls moveCamera passing screen size as parameters
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 10));

Hope it helps someone else!

查看更多
爷的心禁止访问
3楼-- · 2020-01-24 04:17
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

use this it worked for mee

查看更多
forever°为你锁心
4楼-- · 2020-01-24 04:17

I found this to work and be more simple than the other solutions:

private void moveMapToBounds(final CameraUpdate update) {
    try {
        if (movedMap) {
            // Move map smoothly from the current position.
            map.animateCamera(update);
        } else {
            // Move the map immediately to the starting position.
            map.moveCamera(update);
            movedMap = true;
        }
    } catch (IllegalStateException e) {
        // Map may not be laid out yet.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                moveMapToBounds(update);
            }
        });
    }
}

This tries the call again after layout has run. Could probably include a safety to avoid an infinite loop.

查看更多
【Aperson】
5楼-- · 2020-01-24 04:19

As stated in OnCameraChangeListener() is deprecated, setOnCameraChangeListener is now deprecated. So you should replace it with one of three mtehods:

  • GoogleMap.OnCameraMoveStartedListener
  • GoogleMap.OnCameraMoveListener
  • GoogleMap.OnCameraIdleListener

In my case I used OnCameraIdleListener and inside I removed it, because it was invoked again and again on any movement.

googleMap.setOnCameraIdleListener {
    googleMap.setOnCameraIdleListener(null) // It removes the listener.
    googleMap.moveCamera(track)
    googleMap.cameraPosition
    clusterManager!!.cluster()
    // Add another listener to make ClusterManager correctly zoom clusters and markers.
    googleMap.setOnCameraIdleListener(clusterManager)
}

UPDATE

I removed googleMap.setOnCameraIdleListener in my project, because it wasn't called sometimes when a map was shown, but retained googleMap.setOnCameraIdleListener(clusterManager).

查看更多
劫难
6楼-- · 2020-01-24 04:30

The accepted answer is, as pointed out in the comments, a little hacky. After implementing it I noticed an above-acceptable amount of IllegalStateException logs in analytics. The solution I used is to add an OnMapLoadedCallback in which the CameraUpdate is performed. The callback is added to the GoogleMap. After your map fully loads the camera update will be performed.

This does cause the map to briefly show the zoomed out (0,0) view before performing the camera update. I feel that this is more acceptable than causing crashes or relying on undocumented behavior though.

查看更多
甜甜的少女心
7楼-- · 2020-01-24 04:30

If you need to wait for possible user interactions to start, use OnMapLoadedCallbackas described in the previous answers. But, if all you need is to provide a default location for the map, there is no need for any of the solutions outlined in those answers. Both MapFragmentand MapView can accept a GoogleMapOptions at start that can provide the default location all right. The only trick is not to include them directly in your layout because the system will call them without the options then but to initialize them dynamically.

Use this in your layout:

<FrameLayout
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

and replace the fragment in your onCreateView():

GoogleMapOptions options = new GoogleMapOptions();
options.camera(new CameraPosition.Builder().target(location).zoom(15).build());
// other options calls if required

SupportMapFragment fragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map);
if (fragment == null) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  fragment = SupportMapFragment.newInstance(options);
  transaction.replace(R.id.map, fragment).commit();
  getFragmentManager().executePendingTransactions();
}
if (fragment != null)
  GoogleMap map = fragment.getMap();

Besides being faster to start, there will be no world map shown first and a camera move second. The map will start with the specified location directly.

查看更多
登录 后发表回答