android - the - Agregar límites al mapa para evitar deslizar fuera de una región determinada
google maps api key android (9)
Mi aplicación muestra un mapa y quiero que los usuarios no puedan deslizar sobre una determinada región. 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 de creación que causa el bloqueo:
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
Causado por: java.lang.IllegalStateException: Error al utilizar newLatLngBounds (LatLngBounds, int): el tamaño del mapa no puede ser 0. Lo más probable es que todavía no se haya realizado el diseño para la vista del mapa. Espere hasta que se haya realizado el diseño o use newLatLngBounds (LatLngBounds, int, int, int) que le permita especificar las dimensiones del mapa.
De 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 la cámara hasta que el mapa haya sido diseñado (para que este método determine correctamente el cuadro delimitador y el nivel de zoom apropiados, 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 también debe haber tenido un diseño tal que sus dimensiones hayan sido determinadas. Si no puede estar seguro de que esto ha ocurrido, utilice newLatLngBounds (LatLngBounds, int, int, int) en su lugar y proporcione las dimensiones del mapa de forma manual.
Nota: getMap()
podría devolver nulo. Es mejor verificar la disponibilidad de los servicios de Google play antes de inicializar el objeto GoogleMap.
Creé una manera de combinar las dos devoluciones de llamada: onMapReady y onGlobalLayout en una única observación que se emitirá solo cuando se hayan activado ambos eventos.
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
}
La documentación de onMapReady () indica claramente que debe esperar tanto enMapReadyCallback como en ViewTreeObserver.OnGlobalLayoutListener:
Tenga en cuenta que esto no garantiza que el mapa haya sido diseñado. Por lo tanto, el tamaño del mapa puede no haber sido determinado por el momento en que se llama al 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 también ViewTreeObserver.OnGlobalLayoutListener.
Es bastante incómodo, pero una forma de hacerlo sería usando RxJava. Podría emitir dos elementos observables, el primero en MappingReady y el segundo en OnGlobalLayout, luego comprímalos y haga lo que tenga que hacer. De esta manera, puede estar seguro de que ambas devoluciones de llamada se activaron.
La respuesta que está marcada como correcta no es 100% correcta. Creo que todavía puede fallar en los casos en que el mapa nunca se carga debido a la conectividad, o si el mapa se actualiza tan rápidamente que nunca termina de cargarse por completo. He usado addOnGlobalLayoutListener.
ConstraintLayout mapLayout = (ConstraintLayout)findViewById(R.id.activityLayout);
mapLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
//Your map code
}
});
Puede evitar que ocurra el error siguiendo los consejos del registro:
"... use newLatLngBounds (LatLngBounds, int, int, int) que le permite especificar las dimensiones del mapa".
Los parámetros adicionales son el ancho y alto de su pantalla. Así es como se vería su 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);
Puede usar 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 prier arriba.
map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition arg0) {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
}
});
Realice la llamada dentro de un ejecutable y publíquelo en el controlador de vistas, de esta forma:
mapFragment.getView().post(new Runnable() {
@Override
public void run() {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), CAMERA_PADDING));
}
});
Cuando la vista se haya trazado, el ejecutable se ejecutará y posicionará correctamente el mapa. El problema con la respuesta ahmadalibalochs es que verás un destello del globo antes de que se posicione correctamente. Publicarlo en el controlador de vista resuelve este problema.
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 se debe a que el diseño del mapa no se ha completado, debe implementar OnMapReadyCallback en sus llamadas, lo que asegurará que su código solo se ejecutará una vez que se complete su diseño 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
moverCámara con CameraUpdateFactory.newLatLngBounds bloqueos
El tamaño del mapa IllegalStateException no debe ser 0
No lo hago si hay una forma de fusionar este enlace. Espero haber ayudado