web-dev-qa-db-fra.com

Ajouter des limites à la carte pour éviter de glisser en dehors d'une certaine région

Mon application affiche une carte et je veux que les utilisateurs ne puissent pas glisser au-dessus d'une certaine région . Je tente donc d'ajouter des limites, mais l'application provoque le blocage de l'application . Voici le code de travail:

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)));;
        }
    }
}

Et voici le code que j’ajoute à la méthode onCreate qui a provoqué le crash:

    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);

Voici le 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
37
smartmouse

Vous pouvez utiliser MapLoadedCallBack;

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

et aussi vous pouvez utiliser cet événement qui se produit plus tôt.

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

Vous pouvez éviter que l'erreur ne se produise en suivant les conseils du journal:

"... utilise newLatLngBounds (LatLngBounds, int, int, int) qui permet de spécifier les dimensions de la carte."

Les paramètres supplémentaires sont la largeur et la hauteur de votre écran. Voici à quoi ressemblerait votre code mis à jour:

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);
51
Patrick C.

La documentation pour onMapReady () indique clairement que vous devez attendre à la fois onMapReadyCallback et ViewTreeObserver.OnGlobalLayoutListener:

Notez que cela ne garantit pas que la carte a subi une mise en page. Par conséquent, la taille de la carte peut ne pas avoir été déterminée par le moment où la méthode de rappel est appelée. Si vous devez connaître les dimensions ou appeler une méthode dans l'API qui doit connaître les dimensions, obtenez la vue de la carte et enregistrez un ViewTreeObserver.OnGlobalLayoutListener.

C'est très pratique, mais une façon de le faire serait d’utiliser RxJava. Vous pouvez émettre deux observables, le premier dans onMapReady et le second dans onGlobalLayout, puis Zip ensemble et faites ce que vous devez faire. De cette façon, vous pouvez être certain que les deux rappels ont été déclenchés.

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

La raison derrière cette erreur est parce que la présentation de la carte n'a pas été terminée, vous devez implémenter OnMapReadyCallback dans vos appels, ce qui garantira que votre code ne s'exécutera qu'une fois votre présentation terminée ou implémenter le rappel ci-dessous.

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

Liens en double 

moveCamera with CameraUpdateFactory.newLatLngBounds se bloque

La taille de la carte IllegalStateException ne doit pas être 0

Je ne fais pas s'il y a un moyen de fusionner ce lien. J'espère que j'ai aidé

4
Bikash

Causée par: Java.lang.IllegalStateException: Erreur lors de l'utilisation de newLatLngBounds (LatLngBounds, int): la taille de la carte ne peut pas être 0. Très probablement, la mise en page n'a pas encore eu lieu pour la vue cartographique. Soit attendre la mise en page a eu lieu ou utilisez newLatLngBounds (LatLngBounds, int, int, int) qui vous permet de spécifier les dimensions de la carte.

Dans les documents https://developer.Android.com/reference/com/google/Android/gms/maps/CameraUpdateFactory.html#newLatLngBounds(com.google.Android.gms.maps.model.LodLngBounds , int )

Ne changez pas la caméra avec cette mise à jour de la caméra tant que la carte n’a pas mise en page (pour que cette méthode détermine correctement le cadre de sélection et le niveau de zoom appropriés, la carte doit avoir une taille) . Sinon, une exception IllegalStateException sera levée. Ce n'est pas suffisant pour que la mappe soit disponible (par exemple, getMap () renvoie un objet non-null); la vue contenant la carte doit également avoir subi disposition telle que ses dimensions aient été déterminées. Si vous ne pouvez pas être Assurez-vous que cela s’est produit, utilisez plutôt newLatLngBounds (LatLngBounds, int, int, int) et indiquez les dimensions de la carte manuellement.

Remarque: getMap() peut renvoyer null. Il est préférable de vérifier la disponibilité des services Google Play avant d’initialiser l’objet GoogleMap.

4
Raghunandan

Cela a fonctionné pour moi:

@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
}
2
Evgeny Nozdrev

J'ai créé un moyen de combiner les deux callbacks: onMapReady et onGlobalLayout en une seule observable qui ne sera émise que lorsque les deux événements auront été déclenchés. 

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;
        }
    });
}
}
2
Abhay Sood

Placez l'appel dans un environnement exécutable et postez-le dans le gestionnaire de vues, comme suit: 

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

Une fois la vue affichée, le fichier exécutable s’exécutera et positionnera correctement la carte. Le problème avec la réponse ahmadalibalochs est que vous verrez un éclair du globe avant qu’il ne se positionne correctement. L'afficher dans le gestionnaire de vues résout ce problème.

1
McP

La réponse qui est marquée comme correcte n’est pas correcte à 100%. Je pense que cela peut encore échouer dans les cas où la carte ne se charge jamais en raison de la connectivité ou si la carte est mise à jour si rapidement qu'elle ne se termine jamais complètement. J'ai utilisé addOnGlobalLayoutListener.

ConstraintLayout mapLayout = (ConstraintLayout)findViewById(R.id.activityLayout);
mapLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
    @Override
    public void onGlobalLayout()
    {
         //Your map code
    }
});
0
Siddharth