Añadir límites al mapa para evitar pasar fuera de una región determinada

Mi aplicación muestra un mapa y quiero que los usuarios no puedan desplazarse por una región determinada. Así que estoy tratando de agregar límites pero hace que la aplicación se bloquee. Aquí está el código de trabajo:

public class MapViewer extends Activity implements OnInfoWindowClickListener { private LatLng defaultLatLng = new LatLng(42.564241, 12.22759); private GoogleMap map; private int zoomLevel = 5; private Database db = new Database(this); protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mapviewer); try { map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap(); if (map != null) { map.setMyLocationEnabled(true); map.setMapType(GoogleMap.MAP_TYPE_NORMAL); map.getUiSettings().setRotateGesturesEnabled(false); map.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultLatLng, zoomLevel)); this.addMerchantMarkers(new MarkerOptions()); map.setOnInfoWindowClickListener(this); } } catch (NullPointerException e) { e.printStackTrace(); } } @Override public void onPause() { if (map != null) { map.setMyLocationEnabled(false); map.setTrafficEnabled(false); } super.onPause(); } public void addMerchantMarkers(MarkerOptions mo) { SQLiteDatabase dbRead = db.getReadableDatabase(); String[] columns = {"title", "addr", "lat", "lon"}; Cursor result = dbRead.query("merchants", columns, null, null, null, null, null); while(result.moveToNext()) { String merchant = result.getString(0); String address = result.getString(1); float lat = result.getFloat(2); float lon = result.getFloat(3); LatLng pos = new LatLng(lat, lon); map.addMarker(mo.position(pos) .title(merchant) .snippet(address) .icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_50)));; } } } 

Y este es el código que agrego en el método onCreate que causa el accidente:

  LatLngBounds.Builder builder = new LatLngBounds.Builder(); builder.include(new LatLng(47.09194444, 18.52166666)); builder.include(new LatLng(36.448311, 6.62555555)); LatLngBounds bounds = builder.build(); CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 30); map.animateCamera(cu); 

Aquí está el LogCat:

 08-10 20:59:41.689: E/AndroidRuntime(6304): FATAL EXCEPTION: main 08-10 20:59:41.689: E/AndroidRuntime(6304): Process: com.example.myapp, PID: 6304 08-10 20:59:41.689: E/AndroidRuntime(6304): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapp/com.example.myapp.MapViewer}: java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view. Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions. 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2215) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2264) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread.access$800(ActivityThread.java:144) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1205) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.os.Handler.dispatchMessage(Handler.java:102) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.os.Looper.loop(Looper.java:136) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread.main(ActivityThread.java:5139) 08-10 20:59:41.689: E/AndroidRuntime(6304): at java.lang.reflect.Method.invokeNative(Native Method) 08-10 20:59:41.689: E/AndroidRuntime(6304): at java.lang.reflect.Method.invoke(Method.java:515) 08-10 20:59:41.689: E/AndroidRuntime(6304): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:796) 08-10 20:59:41.689: E/AndroidRuntime(6304): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:612) 08-10 20:59:41.689: E/AndroidRuntime(6304): at dalvik.system.NativeStart.main(Native Method) 08-10 20:59:41.689: E/AndroidRuntime(6304): Caused by: java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view. Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions. 08-10 20:59:41.689: E/AndroidRuntime(6304): at mut.b(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at oxp.a(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at oxi.a(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at oyf.b(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at grl.onTransact(SourceFile:92) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.os.Binder.transact(Binder.java:361) 08-10 20:59:41.689: E/AndroidRuntime(6304): at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a.animateCamera(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at com.google.android.gms.maps.GoogleMap.animateCamera(Unknown Source) 08-10 20:59:41.689: E/AndroidRuntime(6304): at com.example.myapp.MapViewer.onCreate(MapViewer.java:59) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.Activity.performCreate(Activity.java:5231) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087) 08-10 20:59:41.689: E/AndroidRuntime(6304): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2169) 08-10 20:59:41.689: E/AndroidRuntime(6304): ... 11 more 

Puede utilizar MapLoadedCallBack;

 map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { @Override public void onMapLoaded() { map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30)); } }); 

Y también puedes usar este evento que ocurre antes de arriba.

  map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() { @Override public void onCameraChange(CameraPosition arg0) { map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30)); } }); 

Puede evitar que se produzca el error siguiendo el consejo del registro:

"… use newLatLngBounds (LatLngBounds, int, int, int) que le permite especificar las dimensiones del mapa."

Los parámetros adicionales son el ancho y la altura de la pantalla. Así es como se vería el código actualizado:

 LatLngBounds.Builder builder = new LatLngBounds.Builder(); builder.include(new LatLng(47.09194444, 18.52166666)); builder.include(new LatLng(36.448311, 6.62555555)); LatLngBounds bounds = builder.build(); // begin new code: int width = getResources().getDisplayMetrics().widthPixels; int height = getResources().getDisplayMetrics().heightPixels; int padding = (int) (width * 0.12); // offset from edges of the map 12% of screen CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding); // end of new code map.animateCamera(cu); 

Causado por: java.lang.IllegalStateException: Error al usar newLatLngBounds (LatLngBounds, int): El tamaño del mapa no puede ser 0. Lo más probable es que aún no se haya producido la vista del mapa. Espere hasta que se haya producido el diseño o utilice newLatLngBounds (LatLngBounds, int, int, int) que le permite especificar las dimensiones del mapa.

Desde los documentos https://developer.android.com/reference/com/google/android/gms/maps/CameraUpdateFactory.html#newLatLngBounds(com.google.android.gms.maps.model.LatLngBounds , int)

No cambie la cámara con esta actualización de cámara hasta que el mapa se haya sometido a diseño (para que este método determine correctamente el cuadro delimitador y el nivel de zoom adecuados, el mapa debe tener un tamaño). De lo contrario se lanzará una IllegalStateException. No es suficiente que el mapa esté disponible (es decir, getMap () devuelve un objeto no nulo); La vista que contiene el mapa debe haberse sometido también a una disposición tal que se hayan determinado sus dimensiones. Si no puede estar seguro de que esto ha ocurrido, utilice newLatLngBounds (LatLngBounds, int, int, int) y proporcione las dimensiones del mapa manualmente.

Nota: getMap() podría devolver null. Es mejor comprobar la disponibilidad de los servicios de reproducción de Google antes de inicializar el objeto GoogleMap.

 Caused by: java.lang.IllegalStateException: Map size should not be 0. Most likely, layout has not yet occured for the map view. 

La razón detrás de este error es porque el diseño del mapa no se ha completado, Debe implementar OnMapReadyCallback en sus llamadas, lo que garantizará que su código sólo se ejecutará después de su diseño se ha completado o Implementar la devolución de llamada a continuación

 map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() { @Override public void onCameraChange(CameraPosition arg0) { map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30)); } }); 

Enlaces duplicados

MoveCamera con CameraUpdateFactory.newLatLngBounds se bloquea

El tamaño del mapa IllegalStateException no debe ser 0

No lo sé si hay una manera de fusionar este enlace. Espero que ayudara

La documentación de onMapReady () indica claramente que hay que esperar a que tanto onMapReadyCallback como ViewTreeObserver.OnGlobalLayoutListener:

Tenga en cuenta que esto no garantiza que el mapa haya sido modificado. Por lo tanto, el tamaño del mapa puede no haber sido determinado por el momento se llama el método de devolución de llamada. Si necesita conocer las dimensiones o llamar a un método en la API que necesita conocer las dimensiones, obtenga la vista del mapa y registre un ViewTreeObserver.OnGlobalLayoutListener también.

Es bastante inconveniente, pero una forma de hacerlo sería mediante el uso de RxJava. Podrías emitir dos observables, primero en onMapReady y segundo en onGlobalLayout, luego rellenarlos juntos y hacer lo que tengas que hacer. De esta manera usted puede estar seguro de que ambos devoluciones fueron despedidos.

Coloque la llamada dentro de un runnable y colóquela en el manejador de vistas, así:

  mapFragment.getView().post(new Runnable() { @Override public void run() { map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), CAMERA_PADDING)); } }); 

Cuando se ha establecido la vista, el runnable ejecutará y posicionará correctamente el mapa. El problema con ahmadalibalochs respuesta, es que usted verá un destello del mundo antes de que se posicione correctamente. Publicarlo en el controlador de vista, resuelve este problema.

He creado una forma de combinar las dos devoluciones de llamada: onMapReady y onGlobalLayout en un único observable que emitirá sólo cuando ambos eventos hayan sido activados.

https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a

 public final class OnMapAndLayoutReady { private OnMapAndLayoutReady() { } /** * Converts {@link OnMapReadyCallback} to an observable. * Note that this method calls {@link MapView#getMapAsync(OnMapReadyCallback)} so you there is no * need to initialize google map view manually. */ private static Observable<GoogleMap> loadMapObservable(final MapView mapView) { return Observable.create(new Observable.OnSubscribe<GoogleMap>() { @Override public void call(final Subscriber<? super GoogleMap> subscriber) { OnMapReadyCallback mapReadyCallback = new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { subscriber.onNext(googleMap); } }; mapView.getMapAsync(mapReadyCallback); } }); } /** * Converts {@link ViewTreeObserver.OnGlobalLayoutListener} to an observable. * This methods also takes care of removing the global layout listener from the view. */ private static Observable<MapView> globalLayoutObservable(final MapView view) { return Observable.create(new Observable.OnSubscribe<MapView>() { @Override public void call(final Subscriber<? super MapView> subscriber) { final ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); subscriber.onNext(view); } }; view.getViewTreeObserver().addOnGlobalLayoutListener(globalLayoutListener); } }); } /** * Takes {@link #globalLayoutObservable(MapView)} and {@link #loadMapObservable(MapView)} and zips their result. * This means that the subscriber will only be notified when both the observables have emitted. */ public static Observable<GoogleMap> onMapAndLayoutReadyObservable(final MapView mapView) { return Observable.zip(globalLayoutObservable(mapView), loadMapObservable(mapView), new Func2<MapView, GoogleMap, GoogleMap>() { @Override public GoogleMap call(MapView mapView, GoogleMap googleMap) { return googleMap; } }); } } 

Esto funcionó para mí:

 @Override protected void onCreate(Bundle savedInstanceState) { ... mMapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mMapFragment.getView().getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override public void onGlobalLayout() { mMapFragment.getView().getViewTreeObserver().removeOnGlobalLayoutListener(this); //Only after onMapReady & onGlobalLayout newLatLngBounds will be available initMapPosition(); //newLatLngBounds here } 
  • Cómo buscar dirección por nombre en Google Map Android
  • Varios mapas v2 en TabActivity
  • Marcador de mapa de bits de centrado (Google Maps Android API v2)
  • El ancho de mi diseño personalizado de InfoWindow se ignora en Google Maps Android API v2
  • Cómo activar la ventana de información personalizada de forma programática en Android
  • Cómo crear los límites de una polilínea de Android para encajar en la pantalla?
  • Cómo centrar el texto en android IconGenerator
  • Fragmento de mapa no se pudo modificar después de la recarga de fragmento
  • Maps de Android ClusteredMarkers - no muestran unclustered?
  • java.lang.ClassCastException: android.support.v4.app.NoSaveStateFrameLayout no se puede convertir en com.google.android.maps.MapView
  • Infowindow personalizado en el mapa de Google android v2
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.